Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/bandit.yaml4
-rw-r--r--.github/workflows/flake8.yaml8
-rw-r--r--.github/workflows/github-pages.yml41
-rw-r--r--.github/workflows/iso-build.yaml18
-rw-r--r--.github/workflows/mypy.yaml14
-rw-r--r--.github/workflows/pytest.yaml8
-rw-r--r--.github/workflows/python-build.yml36
-rw-r--r--.github/workflows/python-publish.yml4
-rw-r--r--.github/workflows/translation-check.yaml28
-rw-r--r--.gitignore3
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.pre-commit-config.yaml42
-rw-r--r--.readthedocs.yaml15
-rw-r--r--.readthedocs.yml22
-rw-r--r--CONTRIBUTING.md13
-rw-r--r--PKGBUILD15
-rw-r--r--README.md291
-rw-r--r--archinstall/__init__.py318
-rw-r--r--archinstall/__main__.py6
-rw-r--r--archinstall/default_profiles/__init__.py (renamed from examples/__init__.py)0
-rw-r--r--archinstall/default_profiles/applications/__init__.py (renamed from profiles/__init__.py)0
-rw-r--r--archinstall/default_profiles/applications/pipewire.py40
-rw-r--r--archinstall/default_profiles/custom.py218
-rw-r--r--archinstall/default_profiles/desktop.py87
-rw-r--r--archinstall/default_profiles/desktops/__init__.py (renamed from profiles/applications/__init__.py)0
-rw-r--r--archinstall/default_profiles/desktops/awesome.py63
-rw-r--r--archinstall/default_profiles/desktops/bspwm.py27
-rw-r--r--archinstall/default_profiles/desktops/budgie.py26
-rw-r--r--archinstall/default_profiles/desktops/cinnamon.py27
-rw-r--r--archinstall/default_profiles/desktops/cutefish.py27
-rw-r--r--archinstall/default_profiles/desktops/deepin.py24
-rw-r--r--archinstall/default_profiles/desktops/enlightenment.py23
-rw-r--r--archinstall/default_profiles/desktops/gnome.py23
-rw-r--r--archinstall/default_profiles/desktops/hyprland.py68
-rw-r--r--archinstall/default_profiles/desktops/i3.py29
-rw-r--r--archinstall/default_profiles/desktops/lxqt.py31
-rw-r--r--archinstall/default_profiles/desktops/mate.py23
-rw-r--r--archinstall/default_profiles/desktops/plasma.py27
-rw-r--r--archinstall/default_profiles/desktops/qtile.py23
-rw-r--r--archinstall/default_profiles/desktops/sway.py77
-rw-r--r--archinstall/default_profiles/desktops/xfce4.py26
-rw-r--r--archinstall/default_profiles/minimal.py15
-rw-r--r--archinstall/default_profiles/profile.py205
-rw-r--r--archinstall/default_profiles/server.py56
-rw-r--r--archinstall/default_profiles/servers/__init__.py0
-rw-r--r--archinstall/default_profiles/servers/cockpit.py19
-rw-r--r--archinstall/default_profiles/servers/docker.py33
-rw-r--r--archinstall/default_profiles/servers/httpd.py19
-rw-r--r--archinstall/default_profiles/servers/lighttpd.py19
-rw-r--r--archinstall/default_profiles/servers/mariadb.py25
-rw-r--r--archinstall/default_profiles/servers/nginx.py19
-rw-r--r--archinstall/default_profiles/servers/postgresql.py26
-rw-r--r--archinstall/default_profiles/servers/sshd.py19
-rw-r--r--archinstall/default_profiles/servers/tomcat.py19
-rw-r--r--archinstall/default_profiles/tailored.py21
-rw-r--r--archinstall/default_profiles/xorg.py34
-rw-r--r--archinstall/lib/boot.py (renamed from archinstall/lib/systemd.py)84
-rw-r--r--archinstall/lib/configuration.py191
-rw-r--r--archinstall/lib/disk/__init__.py55
-rw-r--r--archinstall/lib/disk/blockdevice.py301
-rw-r--r--archinstall/lib/disk/btrfs/__init__.py56
-rw-r--r--archinstall/lib/disk/btrfs/btrfs_helpers.py136
-rw-r--r--archinstall/lib/disk/btrfs/btrfspartition.py109
-rw-r--r--archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py192
-rw-r--r--archinstall/lib/disk/device_handler.py809
-rw-r--r--archinstall/lib/disk/device_model.py1499
-rw-r--r--archinstall/lib/disk/disk_menu.py140
-rw-r--r--archinstall/lib/disk/diskinfo.py40
-rw-r--r--archinstall/lib/disk/dmcryptdev.py48
-rw-r--r--archinstall/lib/disk/encryption.py174
-rw-r--r--archinstall/lib/disk/encryption_menu.py288
-rw-r--r--archinstall/lib/disk/fido.py (renamed from archinstall/lib/hsm/fido.py)75
-rw-r--r--archinstall/lib/disk/filesystem.py622
-rw-r--r--archinstall/lib/disk/helpers.py556
-rw-r--r--archinstall/lib/disk/mapperdev.py92
-rw-r--r--archinstall/lib/disk/partition.py661
-rw-r--r--archinstall/lib/disk/partitioning_menu.py429
-rw-r--r--archinstall/lib/disk/subvolume_menu.py61
-rw-r--r--archinstall/lib/disk/user_guides.py240
-rw-r--r--archinstall/lib/disk/validators.py48
-rw-r--r--archinstall/lib/exceptions.py35
-rw-r--r--archinstall/lib/general.py426
-rw-r--r--archinstall/lib/global_menu.py472
-rw-r--r--archinstall/lib/hardware.py494
-rw-r--r--archinstall/lib/hsm/__init__.py1
-rw-r--r--archinstall/lib/installer.py1881
-rw-r--r--archinstall/lib/interactions/__init__.py19
-rw-r--r--archinstall/lib/interactions/disk_conf.py572
-rw-r--r--archinstall/lib/interactions/general_conf.py209
-rw-r--r--archinstall/lib/interactions/manage_users_conf.py (renamed from archinstall/lib/user_interaction/manage_users_conf.py)41
-rw-r--r--archinstall/lib/interactions/network_menu.py (renamed from archinstall/lib/user_interaction/network_conf.py)110
-rw-r--r--archinstall/lib/interactions/system_conf.py138
-rw-r--r--archinstall/lib/interactions/utils.py39
-rw-r--r--archinstall/lib/locale/__init__.py10
-rw-r--r--archinstall/lib/locale/locale_menu.py158
-rw-r--r--archinstall/lib/locale/utils.py67
-rw-r--r--archinstall/lib/locale_helpers.py168
-rw-r--r--archinstall/lib/luks.py361
-rw-r--r--archinstall/lib/menu/__init__.py11
-rw-r--r--archinstall/lib/menu/abstract_menu.py275
-rw-r--r--archinstall/lib/menu/global_menu.py429
-rw-r--r--archinstall/lib/menu/list_manager.py64
-rw-r--r--archinstall/lib/menu/menu.py253
-rw-r--r--archinstall/lib/menu/simple_menu.py2002
-rw-r--r--archinstall/lib/menu/table_selection_menu.py70
-rw-r--r--archinstall/lib/menu/text_input.py11
-rw-r--r--archinstall/lib/mirrors.py449
-rw-r--r--archinstall/lib/models/__init__.py10
-rw-r--r--archinstall/lib/models/audio_configuration.py54
-rw-r--r--archinstall/lib/models/bootloader.py47
-rw-r--r--archinstall/lib/models/disk_encryption.py90
-rw-r--r--archinstall/lib/models/gen.py (renamed from archinstall/lib/models/dataclasses.py)68
-rw-r--r--archinstall/lib/models/network_configuration.py261
-rw-r--r--archinstall/lib/models/password_strength.py85
-rw-r--r--archinstall/lib/models/pydantic.py134
-rw-r--r--archinstall/lib/models/subvolume.py68
-rw-r--r--archinstall/lib/models/users.py94
-rw-r--r--archinstall/lib/networking.py88
-rw-r--r--archinstall/lib/output.py306
-rw-r--r--archinstall/lib/packages/__init__.py4
-rw-r--r--archinstall/lib/packages/packages.py14
-rw-r--r--archinstall/lib/pacman.py28
-rw-r--r--archinstall/lib/pacman/__init__.py88
-rw-r--r--archinstall/lib/pacman/config.py44
-rw-r--r--archinstall/lib/pacman/repo.py5
-rw-r--r--archinstall/lib/plugins.py91
-rw-r--r--archinstall/lib/profile/__init__.py3
-rw-r--r--archinstall/lib/profile/profile_menu.py218
-rw-r--r--archinstall/lib/profile/profile_model.py39
-rw-r--r--archinstall/lib/profile/profiles_handler.py413
-rw-r--r--archinstall/lib/profiles.py340
-rw-r--r--archinstall/lib/services.py11
-rw-r--r--archinstall/lib/storage.py23
-rw-r--r--archinstall/lib/translationhandler.py20
-rw-r--r--archinstall/lib/udev/__init__.py1
-rw-r--r--archinstall/lib/udev/udevadm.py17
-rw-r--r--archinstall/lib/user_interaction/__init__.py12
-rw-r--r--archinstall/lib/user_interaction/backwards_compatible_conf.py95
-rw-r--r--archinstall/lib/user_interaction/disk_conf.py86
-rw-r--r--archinstall/lib/user_interaction/general_conf.py271
-rw-r--r--archinstall/lib/user_interaction/locale_conf.py42
-rw-r--r--archinstall/lib/user_interaction/partitioning_conf.py362
-rw-r--r--archinstall/lib/user_interaction/save_conf.py135
-rw-r--r--archinstall/lib/user_interaction/subvolume_config.py98
-rw-r--r--archinstall/lib/user_interaction/system_conf.py168
-rw-r--r--archinstall/lib/user_interaction/utils.py79
-rw-r--r--archinstall/lib/utils/__init__.py0
-rw-r--r--archinstall/lib/utils/singleton.py15
-rw-r--r--archinstall/lib/utils/util.py51
-rw-r--r--archinstall/locales/README.md12
-rw-r--r--archinstall/locales/ar/LC_MESSAGES/base.mobin3799 -> 3802 bytes
-rw-r--r--archinstall/locales/ar/LC_MESSAGES/base.po382
-rw-r--r--archinstall/locales/base.pot418
-rw-r--r--archinstall/locales/cs/LC_MESSAGES/base.mobin26115 -> 40248 bytes
-rw-r--r--archinstall/locales/cs/LC_MESSAGES/base.po438
-rw-r--r--archinstall/locales/de/LC_MESSAGES/base.mobin23304 -> 38328 bytes
-rw-r--r--archinstall/locales/de/LC_MESSAGES/base.po791
-rw-r--r--archinstall/locales/el/LC_MESSAGES/base.mobin35562 -> 40243 bytes
-rw-r--r--archinstall/locales/el/LC_MESSAGES/base.po484
-rw-r--r--archinstall/locales/en/LC_MESSAGES/base.mobin261 -> 259 bytes
-rw-r--r--archinstall/locales/en/LC_MESSAGES/base.po374
-rw-r--r--archinstall/locales/es/LC_MESSAGES/base.mobin24393 -> 40515 bytes
-rw-r--r--archinstall/locales/es/LC_MESSAGES/base.po516
-rw-r--r--archinstall/locales/et/LC_MESSAGES/base.mobin0 -> 35078 bytes
-rw-r--r--archinstall/locales/et/LC_MESSAGES/base.po1277
-rw-r--r--archinstall/locales/fi/LC_MESSAGES/base.mobin0 -> 39505 bytes
-rw-r--r--archinstall/locales/fi/LC_MESSAGES/base.po1469
-rw-r--r--archinstall/locales/fr/LC_MESSAGES/base.mobin27591 -> 43163 bytes
-rw-r--r--archinstall/locales/fr/LC_MESSAGES/base.po925
-rw-r--r--archinstall/locales/he/LC_MESSAGES/base.mobin0 -> 44907 bytes
-rw-r--r--archinstall/locales/he/LC_MESSAGES/base.po1257
-rw-r--r--archinstall/locales/hi/LC_MESSAGES/base.mobin0 -> 2098 bytes
-rw-r--r--archinstall/locales/hi/LC_MESSAGES/base.po1181
-rw-r--r--archinstall/locales/hu/LC_MESSAGES/base.mobin0 -> 44297 bytes
-rw-r--r--archinstall/locales/hu/LC_MESSAGES/base.po1247
-rw-r--r--archinstall/locales/id/LC_MESSAGES/base.mobin27433 -> 26261 bytes
-rw-r--r--archinstall/locales/id/LC_MESSAGES/base.po477
-rw-r--r--archinstall/locales/it/LC_MESSAGES/base.mobin26675 -> 41126 bytes
-rw-r--r--archinstall/locales/it/LC_MESSAGES/base.po440
-rw-r--r--archinstall/locales/ja/LC_MESSAGES/base.mobin0 -> 47101 bytes
-rw-r--r--archinstall/locales/ja/LC_MESSAGES/base.po1299
-rw-r--r--archinstall/locales/ka/LC_MESSAGES/base.mobin44957 -> 44826 bytes
-rw-r--r--archinstall/locales/ka/LC_MESSAGES/base.po453
-rw-r--r--archinstall/locales/ko/LC_MESSAGES/base.mobin27355 -> 27224 bytes
-rw-r--r--archinstall/locales/ko/LC_MESSAGES/base.po449
-rw-r--r--archinstall/locales/languages.json12
-rwxr-xr-xarchinstall/locales/locales_generator.sh50
-rw-r--r--archinstall/locales/lt/LC_MESSAGES/base.mobin0 -> 5444 bytes
-rw-r--r--archinstall/locales/lt/LC_MESSAGES/base.po1181
-rw-r--r--archinstall/locales/nl/LC_MESSAGES/base.mobin17688 -> 17691 bytes
-rw-r--r--archinstall/locales/nl/LC_MESSAGES/base.po428
-rw-r--r--archinstall/locales/pl/LC_MESSAGES/base.mobin25444 -> 33089 bytes
-rw-r--r--archinstall/locales/pl/LC_MESSAGES/base.po675
-rw-r--r--archinstall/locales/pt/LC_MESSAGES/base.mobin16333 -> 41058 bytes
-rw-r--r--archinstall/locales/pt/LC_MESSAGES/base.po766
-rw-r--r--archinstall/locales/pt_BR/LC_MESSAGES/base.mobin26469 -> 40509 bytes
-rw-r--r--archinstall/locales/pt_BR/LC_MESSAGES/base.po441
-rw-r--r--archinstall/locales/ro/LC_MESSAGES/base.mobin0 -> 40055 bytes
-rw-r--r--archinstall/locales/ro/LC_MESSAGES/base.po1252
-rw-r--r--archinstall/locales/ru/LC_MESSAGES/base.mobin35760 -> 54724 bytes
-rw-r--r--archinstall/locales/ru/LC_MESSAGES/base.po424
-rw-r--r--archinstall/locales/sv/LC_MESSAGES/base.mobin22729 -> 22732 bytes
-rw-r--r--archinstall/locales/sv/LC_MESSAGES/base.po431
-rw-r--r--archinstall/locales/ta/LC_MESSAGES/base.mobin47610 -> 72950 bytes
-rw-r--r--archinstall/locales/ta/LC_MESSAGES/base.po438
-rw-r--r--archinstall/locales/tr/LC_MESSAGES/base.mobin24422 -> 42356 bytes
-rw-r--r--archinstall/locales/tr/LC_MESSAGES/base.po661
-rw-r--r--archinstall/locales/uk/LC_MESSAGES/base.mobin36158 -> 36027 bytes
-rw-r--r--archinstall/locales/uk/LC_MESSAGES/base.po453
-rw-r--r--archinstall/locales/ur/LC_MESSAGES/base.mobin20487 -> 20490 bytes
-rw-r--r--archinstall/locales/ur/LC_MESSAGES/base.po429
-rw-r--r--archinstall/locales/zh-CN/LC_MESSAGES/base.mobin24126 -> 36412 bytes
-rw-r--r--archinstall/locales/zh-CN/LC_MESSAGES/base.po692
-rw-r--r--archinstall/locales/zh-TW/LC_MESSAGES/base.mobin0 -> 31565 bytes
-rw-r--r--archinstall/locales/zh-TW/LC_MESSAGES/base.po1268
l---------archinstall/profiles1
-rw-r--r--archinstall/scripts/__init__.py0
-rw-r--r--archinstall/scripts/guided.py247
-rw-r--r--archinstall/scripts/list.py10
-rw-r--r--archinstall/scripts/minimal.py106
-rw-r--r--archinstall/scripts/only_hd.py78
-rw-r--r--archinstall/scripts/swiss.py308
-rw-r--r--archinstall/scripts/unattended.py19
-rwxr-xr-xbuild_iso.sh50
-rw-r--r--docs/_static/style.css3
-rw-r--r--docs/_templates/layout.html4
-rw-r--r--docs/archinstall/Application.rst10
-rw-r--r--docs/archinstall/Profile.rst16
-rw-r--r--docs/archinstall/general.rst117
-rw-r--r--docs/archinstall/plugins.rst57
-rw-r--r--docs/cli_parameters/config/config_options.csv24
-rw-r--r--docs/cli_parameters/config/custom_commands.rst22
-rw-r--r--docs/cli_parameters/config/disk_config.rst235
-rw-r--r--docs/cli_parameters/config/disk_encryption.rst19
-rw-r--r--docs/cli_parameters/config/manual_options.csv4
-rw-r--r--docs/conf.py3
-rw-r--r--docs/examples/binary.rst23
-rw-r--r--docs/examples/python.rst83
-rw-r--r--docs/help/known_issues.rst102
-rw-r--r--docs/help/report_bug.rst (renamed from docs/help/issues.rst)2
-rw-r--r--docs/index.rst38
-rw-r--r--docs/installing/binary.rst52
-rw-r--r--docs/installing/guided.rst427
-rw-r--r--docs/installing/python.rst6
-rw-r--r--examples/auto_discovery_mounted.py12
-rw-r--r--examples/config-sample.json115
-rw-r--r--examples/creds-sample.json19
-rw-r--r--examples/custom-command-sample.json5
-rw-r--r--examples/full_automated_installation.py99
-rw-r--r--examples/guided.py306
-rw-r--r--examples/interactive_installation.py206
-rw-r--r--examples/mac_address_installation.py18
-rw-r--r--examples/minimal.py75
-rw-r--r--examples/minimal_installation.py86
-rw-r--r--examples/only_hd.py151
-rw-r--r--examples/only_hd_installation.py61
-rw-r--r--examples/swiss.py526
-rw-r--r--examples/unattended.py21
-rw-r--r--mypy-strict.ini102
-rw-r--r--profiles/52-54-00-12-34-56.py62
-rw-r--r--profiles/applications/awesome.py34
-rw-r--r--profiles/applications/cockpit.py13
-rw-r--r--profiles/applications/docker.py9
-rw-r--r--profiles/applications/httpd.py9
-rw-r--r--profiles/applications/lighttpd.py9
-rw-r--r--profiles/applications/mariadb.py11
-rw-r--r--profiles/applications/nginx.py9
-rw-r--r--profiles/applications/pipewire.py14
-rw-r--r--profiles/applications/postgresql.py11
-rw-r--r--profiles/applications/sshd.py9
-rw-r--r--profiles/applications/tomcat.py12
-rw-r--r--profiles/awesome.py51
-rw-r--r--profiles/bspwm.py43
-rw-r--r--profiles/budgie.py45
-rw-r--r--profiles/cinnamon.py46
-rw-r--r--profiles/cutefish.py41
-rw-r--r--profiles/deepin.py44
-rw-r--r--profiles/desktop.py97
-rw-r--r--profiles/enlightenment.py43
-rw-r--r--profiles/gnome.py45
-rw-r--r--profiles/i3.py59
-rw-r--r--profiles/kde.py58
-rw-r--r--profiles/lxqt.py50
-rw-r--r--profiles/mate.py42
-rw-r--r--profiles/minimal.py24
-rw-r--r--profiles/qtile.py42
-rw-r--r--profiles/server.py63
-rw-r--r--profiles/sway.py98
-rw-r--r--profiles/xfce4.py45
-rw-r--r--profiles/xorg.py68
-rw-r--r--pyproject.toml44
-rw-r--r--renovate.json6
-rw-r--r--schema.json156
293 files changed, 36085 insertions, 14740 deletions
diff --git a/.github/workflows/bandit.yaml b/.github/workflows/bandit.yaml
index 84c63348..fee1f837 100644
--- a/.github/workflows/bandit.yaml
+++ b/.github/workflows/bandit.yaml
@@ -4,9 +4,9 @@ jobs:
flake8:
runs-on: ubuntu-latest
container:
- image: archlinux:latest
+ image: archlinux/archlinux:latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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 d9db80e9..c9346ccc 100644
--- a/.github/workflows/flake8.yaml
+++ b/.github/workflows/flake8.yaml
@@ -4,11 +4,11 @@ jobs:
flake8:
runs-on: ubuntu-latest
container:
- image: archlinux:latest
+ image: archlinux/archlinux:latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: pacman --noconfirm -Syu python python-pip
- - run: python -m pip install --upgrade pip
- - run: pip install flake8
+ - run: pip install --break-system-packages --upgrade pip
+ - run: pip install --break-system-packages flake8
- name: Lint with flake8
run: flake8
diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml
new file mode 100644
index 00000000..65255fd6
--- /dev/null
+++ b/.github/workflows/github-pages.yml
@@ -0,0 +1,41 @@
+name: documentation
+
+on:
+ push:
+ paths:
+ - "docs/**"
+
+ pull_request:
+ paths:
+ - "docs/**"
+
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ docs:
+ runs-on: ubuntu-latest
+ container:
+ image: archlinux/archlinux:latest
+ options: --privileged
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ - name: Install pre-dependencies
+ run: |
+ pacman -Sy --noconfirm tree git python-pyparted python-simple-term-menu python-setuptools python-sphinx python-sphinx_rtd_theme python-build python-installer python-wheel
+ - name: Sphinx build
+ run: |
+ sphinx-build docs _build
+ - name: Deploy to GitHub Pages
+ uses: peaceiris/actions-gh-pages@v4
+ # if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+ with:
+ publish_branch: gh-pages
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: _build/
+ force_orphan: true
+ enable_jekyll: false # This is required to preserve _static (and thus the theme)
+ cname: archinstall.archlinux.page \ No newline at end of file
diff --git a/.github/workflows/iso-build.yaml b/.github/workflows/iso-build.yaml
index ab4e6f5f..b86c47ec 100644
--- a/.github/workflows/iso-build.yaml
+++ b/.github/workflows/iso-build.yaml
@@ -23,27 +23,17 @@ jobs:
build:
runs-on: ubuntu-latest
container:
- image: archlinux:latest
+ image: archlinux/archlinux:latest
options: --privileged
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: pwd
- run: find .
- run: cat /etc/os-release
- run: pacman-key --init
- run: pacman --noconfirm -Sy archlinux-keyring
- - run: mkdir -p /tmp/archlive/airootfs/root/archinstall-git; cp -r . /tmp/archlive/airootfs/root/archinstall-git
- - run: echo "pip uninstall archinstall -y; cd archinstall-git; rm -rf dist; python -m build -n; pip install dist/archinstall*.whl" > /tmp/archlive/airootfs/root/.zprofile
- - run: echo "echo \"This is an unofficial ISO for development and testing of archinstall. No support will be provided.\"" >> /tmp/archlive/airootfs/root/.zprofile
- - run: echo "echo \"This ISO was built from Git SHA $GITHUB_SHA\"" >> /tmp/archlive/airootfs/root/.zprofile
- - run: echo "echo \"Type archinstall to launch the installer.\"" >> /tmp/archlive/airootfs/root/.zprofile
- - run: cat /tmp/archlive/airootfs/root/.zprofile
- - run: pacman -Sy; pacman --noconfirm -S git archiso
- - run: cp -r /usr/share/archiso/configs/releng/* /tmp/archlive
- - run: echo -e "git\npython\npython-pip\npython-build\npython-flit\npython-setuptools\npython-wheel" >> /tmp/archlive/packages.x86_64
- - run: find /tmp/archlive
- - run: cd /tmp/archlive; mkarchiso -v -w work/ -o out/ ./
- - uses: actions/upload-artifact@v3
+ - run: ./build_iso.sh
+ - uses: actions/upload-artifact@v4
with:
name: Arch Live ISO
path: /tmp/archlive/out/*.iso
diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml
index 20c98f3b..39306cd6 100644
--- a/.github/workflows/mypy.yaml
+++ b/.github/workflows/mypy.yaml
@@ -4,19 +4,15 @@ jobs:
mypy:
runs-on: ubuntu-latest
container:
- image: archlinux:latest
+ image: archlinux/archlinux:latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: pacman --noconfirm -Syu python mypy python-pip
- - run: python -m pip install --upgrade pip
- - run: pip install fastapi pydantic
+ - run: pip install --break-system-packages --upgrade pip
+ - run: pip install --break-system-packages fastapi pydantic
- run: python --version
- run: mypy --version
# one day this will be enabled
# run: mypy --strict --module archinstall || exit 0
- name: run mypy
- run: mypy --follow-imports=silent archinstall/lib/menu/abstract_menu.py archinstall/lib/menu/global_menu.py
- archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py archinstall/lib/models/users.py
- archinstall/lib/disk/blockdevice.py archinstall/lib/user_interaction/subvolume_config.py archinstall/lib/disk/btrfs/btrfs_helpers.py
- archinstall/lib/translationhandler.py archinstall/lib/disk/diskinfo.py archinstall/lib/menu/table_selection_menu.py archinstall/lib/hsm
- archinstall/lib/disk/encryption.py archinstall/lib/models/disk_encryption.py
+ run: mypy --config-file pyproject.toml
diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml
index 568a6e6c..6c62dd54 100644
--- a/.github/workflows/pytest.yaml
+++ b/.github/workflows/pytest.yaml
@@ -4,12 +4,12 @@ jobs:
pytest:
runs-on: ubuntu-latest
container:
- image: archlinux:latest
+ image: archlinux/archlinux:latest
options: --privileged
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: pacman --noconfirm -Syu python python-pip qemu gcc
- - run: python -m pip install --upgrade pip
- - run: pip install pytest
+ - run: python -m pip install --break-system-packages --upgrade pip
+ - run: pip install --break-system-packages pytest
- name: Test with pytest
run: python -m pytest || exit 0
diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml
index 647ad70e..483e451b 100644
--- a/.github/workflows/python-build.yml
+++ b/.github/workflows/python-build.yml
@@ -7,20 +7,34 @@ on: [ push, pull_request ]
jobs:
deploy:
runs-on: ubuntu-latest
+ container:
+ image: archlinux/archlinux:latest
+ options: --privileged
steps:
- - uses: actions/checkout@v3
- - name: Set up Python
- uses: actions/setup-python@v4
- with:
- python-version: '3.x'
- - name: Install dependencies
+ - uses: actions/checkout@v4
+ - name: Prepare arch
+ run: |
+ pacman-key --init
+ pacman --noconfirm -Sy archlinux-keyring
+ pacman --noconfirm -Syyu
+ pacman --noconfirm -Sy python-pip python-pyparted python-simple-term-menu pkgconfig gcc
+ - name: Install build dependencies
run: |
- python -m pip install --upgrade pip
- pip install build twine
+ python -m pip install --break-system-packages --upgrade pip
+ pip install --break-system-packages --upgrade build twine wheel setuptools installer
+ pip uninstall archinstall -y --break-system-packages
- name: Build archinstall
+ run: python -m build --wheel --no-isolation
+ - name: Install archinstall
+ run: python -m installer dist/*.whl
+ - name: Run archinstall
run: |
- python -m build . --wheel
- - uses: actions/upload-artifact@v3
+ python -V
+ archinstall --script guided -v
+ archinstall --script swiss -v
+ archinstall --script only_hd -v
+ archinstall --script minimal -v
+ - uses: actions/upload-artifact@v4
with:
name: archinstall
- path: dist/* \ No newline at end of file
+ path: dist/*
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 8a5bd679..0cf71783 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -13,9 +13,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml
new file mode 100644
index 00000000..3cd4d14c
--- /dev/null
+++ b/.github/workflows/translation-check.yaml
@@ -0,0 +1,28 @@
+#on:
+# push:
+# paths:
+# - 'archinstall/locales/**'
+# pull_request:
+# paths:
+# - 'archinstall/locales/**'
+#name: Verify local_generate script was run on translation changes
+#jobs:
+# translation-check:
+# runs-on: ubuntu-latest
+# container:
+# image: archlinux/archlinux:latest
+# steps:
+# - uses: actions/checkout@v4
+# - run: pacman --noconfirm -Syu python git diffutils
+# - name: Verify all translation scripts are up to date
+# run: |
+# cd ..
+# cp -r archinstall archinstall_orig
+# cd archinstall/archinstall/locales
+# bash locales_generator.sh 1> /dev/null
+# cd ../../..
+# git diff \
+# --quiet --no-index --name-only \
+# archinstall_orig/archinstall/locales \
+# archinstall/archinstall/locales \
+# || (echo "Translation files have not been updated after translation, please run ./locales_generator.sh once more and commit" && exit 1)
diff --git a/.gitignore b/.gitignore
index 40e00e87..18173914 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,4 +33,5 @@ venv
**/cmd_history.txt
**/*.*~
/*.sig
-/*.json \ No newline at end of file
+/*.json
+requirements.txt
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ca54c552..ee1b7844 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,7 +4,7 @@
# These jobs should leverage the same tag as that runner. If necessary, change the tag from 'docker' to the one it uses.
# All jobs will be run in the official archlinux container image, so we will declare that here.
-image: archlinux:latest
+image: archlinux/archlinux:latest
# This can be used to handle common actions. In this case, we do a pacman -Sy to make sure repos are ready to use.
before_script:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..32cc9eec
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,42 @@
+default_stages: ['commit']
+repos:
+ - repo: https://github.com/pycqa/autoflake
+ rev: v2.1.1
+ hooks:
+ - id: autoflake
+ args: [
+ '--in-place',
+ '--remove-all-unused-imports',
+ '--ignore-init-module-imports'
+ ]
+ files: \.py$
+ require_serial: true
+ fail_fast: true
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.4.0
+ hooks:
+ # general hooks:
+ - id: check-added-large-files # Prevent giant files from being committed
+ args: ['--maxkb=5000']
+ - id: check-merge-conflict # Check for files that contain merge conflict strings
+ - id: check-symlinks # Checks for symlinks which do not point to anything
+ - id: check-yaml # Attempts to load all yaml files to verify syntax
+ - id: destroyed-symlinks # Detects symlinks which are changed to regular files
+ - id: detect-private-key # Checks for the existence of private keys
+ - id: end-of-file-fixer # Makes sure files end in a newline and only a newline
+ - id: trailing-whitespace # Trims trailing whitespace
+ # Python specific hooks:
+ - id: check-ast # Simply check whether files parse as valid python
+ - id: check-docstring-first # Checks for a common error of placing code before the docstring
+ - repo: https://github.com/pycqa/flake8
+ rev: 6.0.0
+ hooks:
+ - id: flake8
+ args: [--config=.flake8]
+ fail_fast: true
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v1.1.1
+ hooks:
+ - id: mypy
+ args: [--config=pyproject.toml]
+ fail_fast: true
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 00000000..02e90da9
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,15 @@
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+version: 2
+
+sphinx:
+ builder: html
+ configuration: docs/conf.py
+ fail_on_warning: true
+
+build:
+ os: "ubuntu-22.04"
+ tools:
+ python: "3.11" \ No newline at end of file
diff --git a/.readthedocs.yml b/.readthedocs.yml
deleted file mode 100644
index e9a4f4fa..00000000
--- a/.readthedocs.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-# .readthedocs.yml
-# Read the Docs configuration file
-# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
-
-# Required
-version: 2
-
-# Build documentation in the docs/ directory with Sphinx
-sphinx:
- configuration: docs/conf.py
-
-# Build documentation with MkDocs
-#mkdocs:
-# configuration: mkdocs.yml
-
-# Optionally build your docs in additional formats such as PDF
-formats:
- - pdf
-
-# Optionally set the version of Python and requirements required to build your docs
-python:
- version: 3.8
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5ed5142d..3faa8ae8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,8 +27,7 @@ The exceptions to PEP8 are:
* Archinstall uses [tabs instead of spaces](https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces) simply to make it
easier for non-IDE developers to navigate the code *(Tab display-width should be equal to 4 spaces)*. Exception to the
rule are comments that need fine-tuned indentation for documentation purposes.
-* [Line length](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) should aim for no more than 100
- characters, but not strictly enforced.
+* [Line length](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) a maximum line length is enforced via flake8 with 236 characters
* [Line breaks before/after binary operator](https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator)
is not enforced, as long as the style of line breaks is consistent within the same code block.
* Archinstall should always be saved with **Unix-formatted line endings** and no other platform-specific formats.
@@ -39,6 +38,16 @@ The exceptions to PEP8 are:
Most of these style guidelines have been put into place after the fact *(in an attempt to clean up the code)*.<br>
There might therefore be older code which does not follow the coding convention and the code is subject to change.
+## Git hooks
+
+`archinstall` ships pre-commit hooks that make it easier to run check such as `mypy` and `flake8` locally.
+The checks are listed in `.pre-commit-config.yaml` and can be installed via
+```
+pre-commit install
+```
+
+This will install the pre-commit hook and run it every time a `git commit` is executed.
+
## Documentation
If you'd like to contribute to the documentation, refer to [this guide](docs/README.md) on how to build the documentation locally.
diff --git a/PKGBUILD b/PKGBUILD
index 08c2001b..c332d043 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -4,15 +4,27 @@
# Contributor: demostanis worlds <demostanis@protonmail.com>
pkgname=archinstall
-pkgver=2.5.4
+pkgver=2.6.0
pkgrel=1
pkgdesc="Just another guided/automated Arch Linux installer with a twist"
arch=(any)
url="https://github.com/archlinux/archinstall"
license=(GPL3)
depends=(
+ 'arch-install-scripts'
+ 'btrfs-progs'
+ 'coreutils'
+ 'cryptsetup'
+ 'e2fsprogs'
+ 'glibc'
+ 'kbd'
+ 'pciutils'
+ 'procps-ng'
'python'
+ 'python-pyparted'
+ 'python-simple-term-menu'
'systemd'
+ 'util-linux'
)
makedepends=(
'python-setuptools'
@@ -48,7 +60,6 @@ prepare() {
# use real directories for examples and profiles, as symlinks do not work
rm -fv $pkgname/{examples,profiles}
- mv -v examples profiles $pkgname/
}
build() {
diff --git a/README.md b/README.md
index d83a8901..1cad9da3 100644
--- a/README.md
+++ b/README.md
@@ -6,171 +6,204 @@
[![Lint Python and Find Syntax Errors](https://github.com/archlinux/archinstall/actions/workflows/flake8.yaml/badge.svg)](https://github.com/archlinux/archinstall/actions/workflows/flake8.yaml)
Just another guided/automated [Arch Linux](https://wiki.archlinux.org/index.php/Arch_Linux) installer with a twist.
-The installer also doubles as a python library to install Arch Linux and manage services, packages and other things inside the installed system *(Usually from a live medium)*.
+The installer also doubles as a python library to install Arch Linux and manage services, packages, and other things inside the installed system *(Usually from a live medium)*.
* archinstall [discord](https://discord.gg/cqXU88y) server
* archinstall [matrix.org](https://app.element.io/#/room/#archinstall:matrix.org) channel
* archinstall [#archinstall@irc.libera.chat](irc://#archinstall@irc.libera.chat:6697)
-* archinstall [documentation](https://archinstall.readthedocs.io/)
+* archinstall [documentation](https://archinstall.archlinux.page/)
# Installation & Usage
+```shell
+sudo pacman -S archinstall
+```
- $ sudo pacman -S archinstall
+Alternative ways to install are `git clone` the repository or `pip install --upgrade archinstall`.
-Or simply `git clone` the repo as it has no external dependencies *(but there are optional ones)*.<br>
-Or use `pip install --upgrade archinstall` to use as a library.
+## Running the [guided](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) installer
-## Running the [guided](https://github.com/archlinux/archinstall/blob/master/examples/guided.py) installer
+Assuming you are on an Arch Linux live-ISO or installed via `pip`:
+```shell
+archinstall
+```
-Assuming you are on an Arch Linux live-ISO:
+## Running the [guided](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) installer using `git`
- # archinstall
+```shell
+ # cd archinstall-git
+ # python -m archinstall
+```
-Some additional options that are not needed by most users are hidden behind the `--advanced` flag.
+#### Advanced
+Some additional options that most users do not need are hidden behind the `--advanced` flag.
## Running from a declarative configuration file or URL
-Prerequisites:
- 1. Edit the [configuration file](https://github.com/archlinux/archinstall/blob/master/examples/config-sample.json) according to your requirements.
+`archinstall` can be run with a JSON configuration file. There are 2 different configuration files to consider,
+the `user_configuration.json` contains all general installation configuration, whereas the `user_credentials.json`
+contains the sensitive user configuration such as user password, root password, and encryption password.
-Assuming you are on a Arch Linux live-ISO and booted into EFI mode.
+An example of the user configuration file can be found here
+[configuration file](https://github.com/archlinux/archinstall/blob/master/examples/config-sample.json)
+and an example of the credentials configuration here
+[credentials file](https://github.com/archlinux/archinstall/blob/master/examples/creds-sample.json).
- # archinstall --config <path to user config file or URL> --disk-layout <path to disk layout config file or URL> --creds <path to user credentials config file or URL>
-
-# Available Languages
+**HINT:** The configuration files can be auto-generated by starting `archinstall`, configuring all desired menu
+points and then going to `Save configuration`.
-Archinstall is available in different languages which have been contributed and are maintained by the community.
-Current translations are listed below and vary in the amount of translations per language
-```
-English
-Deutsch
-Español
-Français
-Indonesian
-Italiano
-Nederlands
-Polskie
-Português do Brasil
-Português
-Svenska
-Türkçe
-ÄeÅ¡tina
-РуÑÑкий
-اردو
-Ελληνικά
-தமிழà¯
+To load the configuration file into `archinstall` run the following command
+```shell
+archinstall --config <path to user config file or URL> --creds <path to user credentials config file or URL>
```
-Any contributions to the translations are more than welcome, and to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md)
+# Help or Issues
-# Help?
+If you come across any issues, kindly submit your issue here on Github or post your query in the
+[discord](https://discord.gg/cqXU88y) help channel.
-Submit an issue here on GitHub, or submit a post in the discord help channel.<br>
-When doing so, attach the `/var/log/archinstall/install.log` to the issue ticket. This helps us help you!
+When submitting an issue, please:
+* Provide the stacktrace of the output if applicable
+* Attach the `/var/log/archinstall/install.log` to the issue ticket. This helps us help you!
+ * To extract the log from the ISO image, one way is to use<br>
+ ```shell
+ curl -F'file=@/var/log/archinstall/install.log' https://0x0.st
+ ```
-# Mission Statement
-Archinstall promises to ship a [guided installer](https://github.com/archlinux/archinstall/blob/master/examples/guided.py) that follows
-the [Arch Principles](https://wiki.archlinux.org/index.php/Arch_Linux#Principles) as well as a library to manage services, packages and other Arch Linux aspects.
+# Available Languages
-The guided installer will provide user-friendly options along the way, but the keyword here is options, they are optional and will never be forced upon anyone.
-The guided installer itself is also optional to use if so desired and not forced upon anyone.
+Archinstall is available in different languages which have been contributed and are maintained by the community.
+The language can be switched inside the installer (first menu entry). Bear in mind that not all languages provide
+full translations as we rely on contributors to do the translations. Each language has an indicator that shows
+how much has been translated.
----
+Any contributions to the translations are more than welcome,
+to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md)
-Archinstall has one fundamental function which is to be a flexible library to manage services, packages and other aspects inside the installed system.
-This library is in turn used by the provided guided installer but is also for anyone who wants to script their own installations.
+## Fonts
+The ISO does not ship with all fonts needed for different languages.
+Fonts that use a different character set than Latin will not be displayed correctly. If those languages
+want to be selected then a proper font has to be set manually in the console.
-Therefore, Archinstall will try its best to not introduce any breaking changes except for major releases which may break backwards compatibility after notifying about such changes.
+All available console fonts can be found in `/usr/share/kbd/consolefonts` and set with `setfont LatGrkCyr-8x16`.
# Scripting your own installation
-You could just copy [guided.py](https://github.com/archlinux/archinstall/blob/master/examples/guided.py) as a starting point.
-
-However, assuming you're building your own ISO and want to create an automated installation process, or you want to install virtual machines onto local disk images, here is a [minimal example](https://github.com/archlinux/archinstall/blob/master/examples/minimal.py) of how to install using archinstall as a Python library:<br>
+## Scripting interactive installation
-```python
-import archinstall, getpass
-
-# Select a harddrive and a disk password
-harddrive = archinstall.select_disk(archinstall.all_blockdevices(partitions=False))
-disk_password = getpass.getpass(prompt='Disk password (won\'t echo): ')
-
-# We disable safety precautions in the library that protects the partitions
-harddrive.keep_partitions = False
+There are some examples in the `examples/` directory that should serve as a starting point.
-# First, we configure the basic filesystem layout
-with archinstall.Filesystem(harddrive, archinstall.GPT) as fs:
- # We create a filesystem layout that will use the entire drive
- # (this is a helper function, you can partition manually as well)
- fs.use_entire_disk(root_filesystem_type='btrfs')
+The following is a small example of how to script your own *interactive* installation:
- boot = fs.find_partition('/boot')
- root = fs.find_partition('/')
-
- boot.format('vfat')
+```python
+from pathlib import Path
+
+from archinstall import Installer, ProfileConfiguration, profile_handler, User
+from archinstall.default_profiles.minimal import MinimalProfile
+from archinstall.lib.disk.device_model import FilesystemType
+from archinstall.lib.disk.encryption_menu import DiskEncryptionMenu
+from archinstall.lib.disk.filesystem import FilesystemHandler
+from archinstall.lib.interactions.disk_conf import select_disk_config
+
+fs_type = FilesystemType('ext4')
+
+# Select a device to use for the installation
+disk_config = select_disk_config()
+
+# Optional: ask for disk encryption configuration
+data_store = {}
+disk_encryption = DiskEncryptionMenu(disk_config.device_modifications, data_store).run()
+
+# initiate file handler with the disk config and the optional disk encryption config
+fs_handler = FilesystemHandler(disk_config, disk_encryption)
+
+# perform all file operations
+# WARNING: this will potentially format the filesystem and delete all data
+fs_handler.perform_filesystem_operations()
+
+mountpoint = Path('/tmp')
+
+with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=['linux']
+) as installation:
+ installation.mount_ordered_layout()
+ installation.minimal_installation(hostname='minimal-arch')
+ installation.add_additional_packages(['nano', 'wget', 'git'])
+
+ # Optionally, install a profile of choice.
+ # In this case, we install a minimal profile that is empty
+ profile_config = ProfileConfiguration(MinimalProfile())
+ profile_handler.install_profile_config(installation, profile_config)
+
+ user = User('archinstall', 'password', True)
+ installation.create_users(user)
+```
- # Set the flag for encrypted to allow for encryption and then encrypt
- root.encrypted = True
- root.encrypt(password=disk_password)
+This installer will perform the following actions:
-with archinstall.luks2(root, 'luksloop', disk_password) as unlocked_root:
- unlocked_root.format(root.filesystem)
- unlocked_root.mount('/mnt')
+* Prompt the user to configure the disk partitioning
+* Prompt the user to setup disk encryption
+* Create a file handler instance for the configured disk and the optional disk encryption
+* Perform the disk operations (WARNING: this will potentially format the disks and erase all data)
+* Install a basic instance of Arch Linux *(base base-devel linux linux-firmware btrfs-progs efibootmgr)*
+* Install and configures a bootloader to partition 0 on UEFI. On BIOS, it sets the root to partition 0.
+* Install additional packages *(nano, wget, git)*
+* Create a new user
- boot.mount('/mnt/boot')
+> **To create your own ISO with this script in it:** Follow [ArchISO](https://wiki.archlinux.org/index.php/archiso)'s guide on creating your own ISO.
-with archinstall.Installer('/mnt') as installation:
- if installation.minimal_installation(hostname='minimal-arch'):
- installation.add_bootloader()
+## Script non-interactive automated installation
- installation.add_additional_packages(['nano', 'wget', 'git'])
+For an example of a fully scripted, automated installation please refer to the example
+[full_automated_installation.py](https://github.com/archlinux/archinstall/blob/master/examples/full_automated_installation.py)
- # Optionally, install a profile of choice.
- # In this case, we install a minimal profile that is empty
- installation.install_profile('minimal')
+## Unattended installation based on MAC address
- user = User('devel', 'devel', False)
- installation.create_users(user)
- installation.user_set_pw('root', 'airoot')
-```
+Archinstall comes with an [unattended](https://github.com/archlinux/archinstall/blob/master/examples/mac_address_installation.py)
+example which will look for a matching profile for the machine it is being run on, based on any local MAC address.
+For instance, if the machine the code is executed on has the MAC address `52:54:00:12:34:56` it will look for a profile called
+[52-54-00-12-34-56.py](https://github.com/archlinux/archinstall/blob/master/archinstall/default_profiles/tailored.py).
+If it's found, the unattended installation will begin and source that profile as its installation procedure.
-This installer will perform the following:
+# Profiles
-* Prompt the user to select a disk and disk-password
-* Proceed to wipe the selected disk with a `GPT` partition table on a UEFI system and MBR on a BIOS system.
-* Sets up a default 100% used disk with encryption.
-* Installs a basic instance of Arch Linux *(base base-devel linux linux-firmware btrfs-progs efibootmgr)*
-* Installs and configures a bootloader to partition 0 on uefi. On BIOS, it sets the root to partition 0.
-* Install additional packages *(nano, wget, git)*
+`archinstall` comes with a set of pre-configured profiles available for selection during the installation process.
-> **Creating your own ISO with this script on it:** Follow [ArchISO](https://wiki.archlinux.org/index.php/archiso)'s guide on how to create your own ISO.
+- [Desktop](https://github.com/archlinux/archinstall/tree/master/archinstall/default_profiles/desktops)
+- [Server](https://github.com/archlinux/archinstall/tree/master/archinstall/default_profiles/servers)
-## Unattended installation based on MAC address
+The profiles' definitions and the packages they will install can be directly viewed in the menu, or
+[default profiles](https://github.com/archlinux/archinstall/tree/master/archinstall/default_profiles)
-Archinstall comes with an [unattended](https://github.com/archlinux/archinstall/blob/master/examples/unattended.py) example which will look for a matching profile for the machine it is being run on, based on any local MAC address.
-For instance, if the machine that [unattended](https://github.com/archlinux/archinstall/blob/master/examples/unattended.py) is run on has the MAC address `52:54:00:12:34:56` it will look for a profile called [profiles/52-54-00-12-34-56.py](https://github.com/archlinux/archinstall/blob/master/profiles/52-54-00-12-34-56.py).
-If it's found, the unattended installation will commence and source that profile as its installation procedure.
# Testing
## Using a Live ISO Image
-If you want to test a commit, branch or bleeding edge release from the repository using the vanilla Arch Live ISO image,
-you can replace the version of archinstall with a new version and run that with the steps described below:
+If you want to test a commit, branch, or bleeding edge release from the repository using the standard Arch Linux Live ISO image,
+replace the archinstall version with a newer one and execute the subsequent steps defined below.
+
+*Note: When booting from a live USB, the space on the ramdisk is limited and may not be sufficient to allow
+running a re-installation or upgrade of the installer. In case one runs into this issue, any of the following can be used
+- Resize the root partition https://wiki.archlinux.org/title/Archiso#Adjusting_the_size_of_the_root_file_system
+- The boot parameter `copytoram=y` (https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio-archiso/-/blob/master/docs/README.bootparams#L26)
+can be specified which will copy the root filesystem to tmpfs.*
1. You need a working network connection
-2. Install the build requirements with `pacman -Sy; pacman -S git python-pip`
+2. Install the build requirements with `pacman -Sy; pacman -S git python-pip gcc pkgconf`
*(note that this may or may not work depending on your RAM and current state of the squashfs maximum filesystem free space)*
-3. Uninstall the previous version of archinstall with `pip uninstall archinstall`
+3. Uninstall the previous version of archinstall with `pip uninstall --break-system-packages archinstall`
4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall`
5. Enter the repository with `cd archinstall`
*At this stage, you can choose to check out a feature branch for instance with `git checkout v2.3.1-rc1`*
-6. Build the project and install it using `python setup.py install`
- *If you get a 'No Module named setuptools' error, run `pacman -S python-setuptools`*
-
-After this, running archinstall with `python -m archinstall` will run against whatever branch you chose in step 5.
+6. To run the source code, there are 2 different options:
+ - Run a specific branch version from source directly using `python -m archinstall`, in most cases this will work just fine, the
+ rare case it will not work is if the source has introduced any new dependencies that are not installed yet
+ - Installing the branch version with `pip install --break-system-packages .` and `archinstall`
## Without a Live ISO Image
@@ -178,8 +211,7 @@ To test this without a live ISO, the simplest approach is to use a local image a
This can be done by installing `pacman -S arch-install-scripts util-linux` locally and doing the following:
# truncate -s 20G testimage.img
- # losetup -fP ./testimage.img
- # losetup -a | grep "testimage.img" | awk -F ":" '{print $1}'
+ # losetup --partscan --show --find ./testimage.img
# pip install --upgrade archinstall
# python -m archinstall --script guided
# qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_VARS.fd
@@ -190,3 +222,44 @@ This will create a *20 GB* `testimage.img` and create a loop device which we can
There's also a [Building and Testing](https://github.com/archlinux/archinstall/wiki/Building-and-Testing) guide.<br>
It will go through everything from packaging, building and running *(with qemu)* the installer against a dev branch.
+
+
+# FAQ
+
+## How to dual boot with Windows
+
+To install Arch Linux alongside an existing Windows installation using `archinstall`, follow these steps:
+
+1. Ensure some unallocated space is available for the Linux installation after the Windows installation.
+2. Boot into the ISO and run `archinstall`.
+3. Choose `Disk configuration` -> `Manual partitioning`.
+4. Select the disk on which Windows resides.
+5. Select `Create a new partition`.
+6. Choose a filesystem type.
+7. Determine the start and end sectors for the new partition location (values can be suffixed with various units).
+8. Assign the mountpoint `/` to the new partition.
+9. Assign the `Boot/ESP` partition the mountpoint `/boot` from the partitioning menu.
+10. Confirm your settings and exit to the main menu by choosing `Confirm and exit`.
+11. Modify any additional settings for your installation as necessary.
+12. Start the installation upon completion of setup.
+
+
+# Mission Statement
+
+Archinstall promises to ship a [guided installer](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) that follows
+the [Arch Linux Principles](https://wiki.archlinux.org/index.php/Arch_Linux#Principles) as well as a library to manage services, packages, and other Arch Linux aspects.
+
+The guided installer ensures a user-friendly experience, offering optional selections throughout the process. Emphasizing its flexible nature, these options are never obligatory.
+In addition, the decision to use the guided installer remains entirely with the user, reflecting the Linux philosophy of providing full freedom and flexibility.
+
+---
+
+Archinstall primarily functions as a flexible library for managing services, packages, and other elements within an Arch Linux system.
+This core library is the backbone for the guided installer that Archinstall provides. It is also designed to be used by those who wish to script their own custom installations.
+
+Therefore, Archinstall will try its best to not introduce any breaking changes except for major releases which may break backward compatibility after notifying about such changes.
+
+
+# Contributing
+
+Please see [CONTRIBUTING.md](https://github.com/archlinux/archinstall/blob/master/CONTRIBUTING.md)
diff --git a/archinstall/__init__.py b/archinstall/__init__.py
index 9de4a3ec..1c980390 100644
--- a/archinstall/__init__.py
+++ b/archinstall/__init__.py
@@ -1,59 +1,70 @@
"""Arch Linux installer - guided, templates etc."""
-import typing
+import importlib
+import os
+import sys
+import time
+import traceback
from argparse import ArgumentParser, Namespace
-
-from .lib.disk import *
-from .lib.exceptions import *
-from .lib.general import *
-from .lib.hardware import *
-from .lib.installer import __packages__, Installer, accessibility_tools_in_use
-from .lib.locale_helpers import *
-from .lib.luks import *
-from .lib.mirrors import *
-from .lib.models.network_configuration import NetworkConfigurationHandler
-from .lib.models.users import User
-from .lib.networking import *
-from .lib.output import *
-from .lib.models.dataclasses import (
- VersionDef,
- PackageSearchResult,
- PackageSearch,
- LocalPackage
-)
-from .lib.packages.packages import (
- group_search,
- package_search,
- find_package,
- find_packages,
- installed_package,
- validate_package_list,
-)
-from .lib.profiles import *
-from .lib.services import *
-from .lib.storage import *
-from .lib.systemd import *
-from .lib.user_interaction import *
-from .lib.menu import Menu
-from .lib.menu.list_manager import ListManager
-from .lib.menu.text_input import TextInput
-from .lib.menu.global_menu import GlobalMenu
-from .lib.menu.abstract_menu import (
- Selector,
- AbstractMenu
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Dict, Union
+
+from .lib import disk
+from .lib import menu
+from .lib import models
+from .lib import packages
+from .lib import exceptions
+from .lib import luks
+from .lib import locale
+from .lib import mirrors
+from .lib import networking
+from .lib import profile
+from .lib import interactions
+from . import default_profiles
+
+from .lib.hardware import SysInfo, GfxDriver
+from .lib.installer import Installer, accessibility_tools_in_use
+from .lib.output import FormattedOutput, log, error, debug, warn, info
+from .lib.pacman import Pacman
+from .lib.storage import storage
+from .lib.global_menu import GlobalMenu
+from .lib.boot import Boot
+from .lib.translationhandler import TranslationHandler, Language, DeferredTranslation
+from .lib.plugins import plugins, load_plugin
+from .lib.configuration import ConfigurationOutput
+
+from .lib.general import (
+ generate_password, locate_binary, clear_vt100_escape_codes,
+ JSON, UNSAFE_JSON, SysCommandWorker, SysCommand,
+ run_custom_user_commands, json_stream_to_structure, secret
)
-from .lib.translationhandler import TranslationHandler, DeferredTranslation
-from .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony
-from .lib.configuration import *
-from .lib.udev import udevadm_info
-parser = ArgumentParser()
-__version__ = "2.5.4"
+if TYPE_CHECKING:
+ _: Any
+
+
+__version__ = "2.8.0"
storage['__version__'] = __version__
-# add the custome _ as a builtin, it can now be used anywhere in the
+# add the custom _ as a builtin, it can now be used anywhere in the
# project to mark strings as translatable with _('translate me')
DeferredTranslation.install()
+# Log various information about hardware before starting the installation. This might assist in troubleshooting
+debug(f"Hardware model detected: {SysInfo.sys_vendor()} {SysInfo.product_name()}; UEFI mode: {SysInfo.has_uefi()}")
+debug(f"Processor model detected: {SysInfo.cpu_model()}")
+debug(f"Memory statistics: {SysInfo.mem_available()} available out of {SysInfo.mem_total()} total installed")
+debug(f"Virtualization detected: {SysInfo.virtualization()}; is VM: {SysInfo.is_vm()}")
+debug(f"Graphics devices detected: {SysInfo._graphics_devices().keys()}")
+
+# For support reasons, we'll log the disk layout pre installation to match against post-installation layout
+debug(f"Disk states before installing: {disk.disk_layouts()}")
+
+if 'sphinx' not in sys.modules and os.getuid() != 0:
+ print(_("Archinstall requires root privileges to run. See --help for more."))
+ exit(1)
+
+parser = ArgumentParser()
+
def define_arguments():
"""
@@ -66,20 +77,25 @@ def define_arguments():
parser.add_argument("-v", "--version", action="version", version="%(prog)s " + __version__)
parser.add_argument("--config", nargs="?", help="JSON configuration file or URL")
parser.add_argument("--creds", nargs="?", help="JSON credentials configuration file")
- parser.add_argument("--disk_layouts","--disk_layout","--disk-layouts","--disk-layout",nargs="?",
- help="JSON disk layout file")
parser.add_argument("--silent", action="store_true",
help="WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored")
parser.add_argument("--dry-run", "--dry_run", action="store_true",
help="Generates a configuration file and then exits instead of performing an installation")
parser.add_argument("--script", default="guided", nargs="?", help="Script to run for installation", type=str)
- parser.add_argument("--mount-point","--mount_point", nargs="?", type=str, help="Define an alternate mount point for installation")
+ parser.add_argument("--mount-point", "--mount_point", nargs="?", type=str,
+ help="Define an alternate mount point for installation")
+ parser.add_argument("--skip-ntp", action="store_true", help="Disables NTP checks during installation", default=False)
parser.add_argument("--debug", action="store_true", default=False, help="Adds debug info into the log")
- parser.add_argument("--offline", action="store_true", default=False, help="Disabled online upstream services such as package search and key-ring auto update.")
- parser.add_argument("--no-pkg-lookups", action="store_true", default=False, help="Disabled package validation specifically prior to starting installation.")
+ parser.add_argument("--offline", action="store_true", default=False,
+ help="Disabled online upstream services such as package search and key-ring auto update.")
+ parser.add_argument("--no-pkg-lookups", action="store_true", default=False,
+ help="Disabled package validation specifically prior to starting installation.")
parser.add_argument("--plugin", nargs="?", type=str)
+ parser.add_argument("--skip-version-check", action="store_true",
+ help="Skip the version check when running archinstall")
+
-def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, error :bool = False) -> dict:
+def parse_unspecified_argument_list(unknowns: list, multiple: bool = False, err: bool = False) -> dict:
"""We accept arguments not defined to the parser. (arguments "ad hoc").
Internally argparse return to us a list of words so we have to parse its contents, manually.
We accept following individual syntax for each argument
@@ -95,51 +111,52 @@ def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, erro
argument value value ...
which isn't am error if multiple is specified
"""
- tmp_list = unknowns[:] # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method()
+ tmp_list = unknowns[:] # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method()
config = {}
key = None
last_key = None
while tmp_list:
- element = tmp_list.pop(0) # retrieve an element of the list
- if element.startswith('--'): # is an argument ?
- if '=' in element: # uses the arg=value syntax ?
+ element = tmp_list.pop(0) # retrieve an element of the list
+ if element.startswith('--'): # is an argument ?
+ if '=' in element: # uses the arg=value syntax ?
key, value = [x.strip() for x in element[2:].split('=', 1)]
config[key] = value
- last_key = key # for multiple handling
- key = None # we have the kwy value pair we need
+ last_key = key # for multiple handling
+ key = None # we have the kwy value pair we need
else:
key = element[2:]
- config[key] = True # every argument starts its lifecycle as boolean
+ config[key] = True # every argument starts its lifecycle as boolean
else:
if element == '=':
continue
if key:
config[key] = element
- last_key = key # multiple
+ last_key = key # multiple
key = None
else:
if multiple and last_key:
- if isinstance(config[last_key],str):
- config[last_key] = [config[last_key],element]
+ if isinstance(config[last_key], str):
+ config[last_key] = [config[last_key], element]
else:
config[last_key].append(element)
- elif error:
+ elif err:
raise ValueError(f"Entry {element} is not related to any argument")
else:
print(f" We ignore the entry {element} as it isn't related to any argument")
return config
-def cleanup_empty_args(args :typing.Union[Namespace, dict]) -> dict:
+
+def cleanup_empty_args(args: Union[Namespace, Dict]) -> Dict:
"""
Takes arguments (dictionary or argparse Namespace) and removes any
None values. This ensures clean mergers during dict.update(args)
"""
- if type(args) == Namespace:
+ if type(args) is Namespace:
args = vars(args)
clean_args = {}
for key, val in args.items():
- if type(val) == dict:
+ if isinstance(val, dict):
val = cleanup_empty_args(val)
if val is not None:
@@ -147,6 +164,7 @@ def cleanup_empty_args(args :typing.Union[Namespace, dict]) -> dict:
return clean_args
+
def get_arguments() -> Dict[str, Any]:
""" The handling of parameters from the command line
Is done on following steps:
@@ -161,7 +179,7 @@ def get_arguments() -> Dict[str, Any]:
3) Amend
Change whatever is needed on the configuration dictionary (it could be done in post_process_arguments but this ougth to be left to changes anywhere else in the code, not in the arguments dictionary
"""
- config = {}
+ config: Dict[str, Any] = {}
args, unknowns = parser.parse_known_args()
# preprocess the JSON files.
# TODO Expand the url access to the other JSON file arguments ?
@@ -174,15 +192,15 @@ def get_arguments() -> Dict[str, Any]:
exit(1)
# load the parameters. first the known, then the unknowns
- args = cleanup_empty_args(args)
- config.update(args)
+ clean_args = cleanup_empty_args(args)
+ config.update(clean_args)
config.update(parse_unspecified_argument_list(unknowns))
# amend the parameters (check internal consistency)
# Installation can't be silent if config is not passed
- if args.get('config') is None:
+ if clean_args.get('config') is None:
config["silent"] = False
else:
- config["silent"] = args.get('silent')
+ config["silent"] = clean_args.get('silent')
# avoiding a compatibility issue
if 'dry-run' in config:
@@ -190,60 +208,52 @@ def get_arguments() -> Dict[str, Any]:
return config
+
def load_config():
"""
refine and set some arguments. Formerly at the scripts
"""
from .lib.models import NetworkConfiguration
+ arguments['locale_config'] = locale.LocaleConfiguration.parse_arg(arguments)
+
if (archinstall_lang := arguments.get('archinstall-language', None)) is not None:
arguments['archinstall-language'] = TranslationHandler().get_language_by_name(archinstall_lang)
- if arguments.get('harddrives', None) is not None:
- if type(arguments['harddrives']) is str:
- arguments['harddrives'] = arguments['harddrives'].split(',')
- arguments['harddrives'] = [BlockDevice(BlockDev) for BlockDev in arguments['harddrives']]
- # Temporarily disabling keep_partitions if config file is loaded
- # Temporary workaround to make Desktop Environments work
+ if disk_config := arguments.get('disk_config', {}):
+ arguments['disk_config'] = disk.DiskLayoutConfiguration.parse_arg(disk_config)
- if arguments.get('profile', None) is not None:
- if type(arguments.get('profile', None)) is dict:
- arguments['profile'] = Profile(None, arguments.get('profile', None)['path'])
- else:
- arguments['profile'] = Profile(None, arguments.get('profile', None))
+ if profile_config := arguments.get('profile_config', None):
+ arguments['profile_config'] = profile.ProfileConfiguration.parse_arg(profile_config)
- storage['_desktop_profile'] = arguments.get('desktop-environment', None)
-
- if arguments.get('mirror-region', None) is not None:
- if type(arguments.get('mirror-region', None)) is dict:
- arguments['mirror-region'] = arguments.get('mirror-region', None)
- else:
- selected_region = arguments.get('mirror-region', None)
- arguments['mirror-region'] = {selected_region: list_mirrors()[selected_region]}
-
- arguments.setdefault('sys-language', 'en_US')
- arguments.setdefault('sys-encoding', 'utf-8')
-
- if arguments.get('gfx_driver', None) is not None:
- storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(arguments.get('gfx_driver', None), None)
+ if mirror_config := arguments.get('mirror_config', None):
+ arguments['mirror_config'] = mirrors.MirrorConfiguration.parse_args(mirror_config)
if arguments.get('servers', None) is not None:
storage['_selected_servers'] = arguments.get('servers', None)
- if arguments.get('nic', None) is not None:
- handler = NetworkConfigurationHandler()
- handler.parse_arguments(arguments.get('nic'))
- arguments['nic'] = handler.configuration
+ if (net_config := arguments.get('network_config', None)) is not None:
+ config = NetworkConfiguration.parse_arg(net_config)
+ arguments['network_config'] = config
if arguments.get('!users', None) is not None or arguments.get('!superusers', None) is not None:
users = arguments.get('!users', None)
superusers = arguments.get('!superusers', None)
- arguments['!users'] = User.parse_arguments(users, superusers)
+ arguments['!users'] = models.User.parse_arguments(users, superusers)
+
+ if arguments.get('bootloader', None) is not None:
+ arguments['bootloader'] = models.Bootloader.from_arg(arguments['bootloader'])
- if arguments.get('disk_encryption', None) is not None and arguments.get('disk_layouts', None) is not None:
+ if arguments.get('uki') and not arguments['bootloader'].has_uki_support():
+ arguments['uki'] = False
+
+ if arguments.get('audio_config', None) is not None:
+ arguments['audio_config'] = models.AudioConfiguration.parse_arg(arguments['audio_config'])
+
+ if arguments.get('disk_encryption', None) is not None and disk_config is not None:
password = arguments.get('encryption_password', '')
- arguments['disk_encryption'] = DiskEncryption.parse_arg(
- arguments['disk_layouts'],
+ arguments['disk_encryption'] = disk.DiskEncryption.parse_arg(
+ arguments['disk_config'],
arguments['disk_encryption'],
password
)
@@ -251,62 +261,84 @@ def load_config():
def post_process_arguments(arguments):
storage['arguments'] = arguments
- if arguments.get('mount_point'):
- storage['MOUNT_POINT'] = arguments['mount_point']
+ if mountpoint := arguments.get('mount_point', None):
+ storage['MOUNT_POINT'] = Path(mountpoint)
if arguments.get('debug', False):
- log(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!", fg="red", level=logging.WARNING)
+ warn(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!")
if arguments.get('plugin', None):
- load_plugin(arguments['plugin'])
-
- if arguments.get('disk_layouts', None) is not None:
- layout_storage = {}
- if not json_stream_to_structure('--disk_layouts',arguments['disk_layouts'],layout_storage):
- exit(1)
- else:
- if arguments.get('harddrives') is None:
- arguments['harddrives'] = [disk for disk in layout_storage]
- # backward compatibility. Change partition.format for partition.wipe
- for disk in layout_storage:
- for i, partition in enumerate(layout_storage[disk].get('partitions',[])):
- if 'format' in partition:
- partition['wipe'] = partition['format']
- del partition['format']
- elif 'btrfs' in partition:
- partition['btrfs']['subvolumes'] = Subvolume.parse_arguments(partition['btrfs']['subvolumes'])
- arguments['disk_layouts'] = layout_storage
+ path = arguments['plugin']
+ load_plugin(path)
load_config()
define_arguments()
-arguments = get_arguments()
+arguments: Dict[str, Any] = get_arguments()
post_process_arguments(arguments)
+
# @archinstall.plugin decorator hook to programmatically add
-# plugins in runtime. Useful in profiles and other things.
+# plugins in runtime. Useful in profiles_bck and other things.
def plugin(f, *args, **kwargs):
plugins[f.__name__] = f
-def run_as_a_module():
+def _check_new_version():
+ info("Checking version...")
+
+ try:
+ Pacman.run("-Sy")
+ except Exception as e:
+ debug(f'Failed to perform version check: {e}')
+ info(f'Arch Linux mirrors are not reachable. Please check your internet connection')
+ exit(1)
+
+ upgrade = None
+
+ try:
+ upgrade = Pacman.run("-Qu archinstall").decode()
+ except Exception as e:
+ debug(f'Failed determine pacman version: {e}')
+
+ if upgrade:
+ text = f'New version available: {upgrade}'
+ info(text)
+ time.sleep(3)
+
+
+def main():
"""
- Since we're running this as a 'python -m archinstall' module OR
- a nuitka3 compiled version of the project.
- This function and the file __main__ acts as a entry point.
+ This can either be run as the compiled and installed application: python setup.py install
+ OR straight as a module: python -m archinstall
+ In any case we will be attempting to load the provided script to be run from the scripts/ folder
"""
+ if not arguments.get('skip_version_check', False):
+ _check_new_version()
- # Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.
- storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))
- try:
- script = Script(arguments.get('script', None))
- except ProfileNotFound as err:
- print(f"Couldn't find file: {err}")
- sys.exit(1)
+ script = arguments.get('script', None)
+
+ if script is None:
+ print('No script to run provided')
- os.chdir(os.path.abspath(os.path.dirname(__file__)))
+ mod_name = f'archinstall.scripts.{script}'
+ # by loading the module we'll automatically run the script
+ importlib.import_module(mod_name)
+
+
+def run_as_a_module():
+ try:
+ main()
+ except Exception as e:
+ err = ''.join(traceback.format_exception(e))
+ error(err)
+
+ text = (
+ 'Archinstall experienced the above error. If you think this is a bug, please report it to\n'
+ 'https://github.com/archlinux/archinstall and include the log file "/var/log/archinstall/install.log".\n\n'
+ 'Hint: To extract the log from a live ISO \ncurl -F\'file=@/var/log/archinstall/install.log\' https://0x0.st\n'
+ )
- # Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.
- storage['PROFILE_PATH'].pop()
- script.execute()
+ warn(text)
+ exit(1)
diff --git a/archinstall/__main__.py b/archinstall/__main__.py
index e125930f..69a79855 100644
--- a/archinstall/__main__.py
+++ b/archinstall/__main__.py
@@ -5,9 +5,13 @@ import pathlib
# Load .git version before the builtin version
if pathlib.Path('./archinstall/__init__.py').absolute().exists():
spec = importlib.util.spec_from_file_location("archinstall", "./archinstall/__init__.py")
+
+ if spec is None or spec.loader is None:
+ raise ValueError('Could not retrieve spec from file: archinstall/__init__.py')
+
archinstall = importlib.util.module_from_spec(spec)
sys.modules["archinstall"] = archinstall
- spec.loader.exec_module(sys.modules["archinstall"])
+ spec.loader.exec_module(archinstall)
else:
import archinstall
diff --git a/examples/__init__.py b/archinstall/default_profiles/__init__.py
index e69de29b..e69de29b 100644
--- a/examples/__init__.py
+++ b/archinstall/default_profiles/__init__.py
diff --git a/profiles/__init__.py b/archinstall/default_profiles/applications/__init__.py
index e69de29b..e69de29b 100644
--- a/profiles/__init__.py
+++ b/archinstall/default_profiles/applications/__init__.py
diff --git a/archinstall/default_profiles/applications/pipewire.py b/archinstall/default_profiles/applications/pipewire.py
new file mode 100644
index 00000000..4cb75968
--- /dev/null
+++ b/archinstall/default_profiles/applications/pipewire.py
@@ -0,0 +1,40 @@
+from typing import List, Union, Any, TYPE_CHECKING
+
+import archinstall
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+from archinstall.lib.models import User
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class PipewireProfile(Profile):
+ def __init__(self):
+ super().__init__('Pipewire', ProfileType.Application)
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ 'pipewire',
+ 'pipewire-alsa',
+ 'pipewire-jack',
+ 'pipewire-pulse',
+ 'gst-plugin-pipewire',
+ 'libpulse',
+ 'wireplumber'
+ ]
+
+ def _enable_pipewire_for_all(self, install_session: 'Installer'):
+ users: Union[User, List[User]] = archinstall.arguments.get('!users', [])
+ if not isinstance(users, list):
+ users = [users]
+
+ for user in users:
+ install_session.arch_chroot('systemctl enable --user pipewire-pulse.service', run_as=user.username)
+
+ def install(self, install_session: 'Installer'):
+ super().install(install_session)
+ install_session.add_additional_packages(self.packages)
+ self._enable_pipewire_for_all(install_session)
diff --git a/archinstall/default_profiles/custom.py b/archinstall/default_profiles/custom.py
new file mode 100644
index 00000000..5f9db620
--- /dev/null
+++ b/archinstall/default_profiles/custom.py
@@ -0,0 +1,218 @@
+# from typing import List, Dict, Optional, TYPE_CHECKING, Any
+#
+# from ..lib import menu
+# from archinstall.lib.output import log, FormattedOutput
+# from archinstall.lib.profile.profiles_handler import profile_handler
+# from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, ProfileInfo, TProfile
+#
+# if TYPE_CHECKING:
+# from archinstall.lib.installer import Installer
+# _: Any
+#
+#
+# class CustomProfileList(menu.ListManager):
+# def __init__(self, prompt: str, profiles: List[TProfile]):
+# self._actions = [
+# str(_('Add profile')),
+# str(_('Edit profile')),
+# str(_('Delete profile'))
+# ]
+# super().__init__(prompt, profiles, [self._actions[0]], self._actions[1:])
+#
+# def reformat(self, data: List[TProfile]) -> Dict[str, Optional[TProfile]]:
+# table = FormattedOutput.as_table(data)
+# rows = table.split('\n')
+#
+# # these are the header rows of the table and do not map to any profile obviously
+# # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
+# # the selectable rows so the header has to be aligned
+# display_data: Dict[str, Optional[TProfile]] = {f' {rows[0]}': None, f' {rows[1]}': None}
+#
+# for row, profile in zip(rows[2:], data):
+# row = row.replace('|', '\\|')
+# display_data[row] = profile
+#
+# return display_data
+#
+# def selected_action_display(self, profile: TProfile) -> str:
+# return profile.name
+#
+# def handle_action(
+# self,
+# action: str,
+# entry: Optional['CustomTypeProfile'],
+# data: List['CustomTypeProfile']
+# ) -> List['CustomTypeProfile']:
+# if action == self._actions[0]: # add
+# new_profile = self._add_profile()
+# if new_profile is not None:
+# # in case a profile with the same name as an existing profile
+# # was created we'll replace the existing one
+# data = [d for d in data if d.name != new_profile.name]
+# data += [new_profile]
+# elif entry is not None:
+# if action == self._actions[1]: # edit
+# new_profile = self._add_profile(entry)
+# if new_profile is not None:
+# # we'll remove the original profile and add the modified version
+# data = [d for d in data if d.name != entry.name and d.name != new_profile.name]
+# data += [new_profile]
+# elif action == self._actions[2]: # delete
+# data = [d for d in data if d != entry]
+#
+# return data
+#
+# def _is_new_profile_name(self, name: str) -> bool:
+# existing_profile = profile_handler.get_profile_by_name(name)
+# if existing_profile is not None and existing_profile.profile_type != ProfileType.CustomType:
+# return False
+# return True
+#
+# def _add_profile(self, editing: Optional['CustomTypeProfile'] = None) -> Optional['CustomTypeProfile']:
+# name_prompt = '\n\n' + str(_('Profile name: '))
+#
+# while True:
+# profile_name = menu.TextInput(name_prompt, editing.name if editing else '').run().strip()
+#
+# if not profile_name:
+# return None
+#
+# if not self._is_new_profile_name(profile_name):
+# error_prompt = str(_("The profile name you entered is already in use. Try again"))
+# print(error_prompt)
+# else:
+# break
+#
+# packages_prompt = str(_('Packages to be install with this profile (space separated, leave blank to skip): '))
+# edit_packages = ' '.join(editing.packages) if editing else ''
+# packages = menu.TextInput(packages_prompt, edit_packages).run().strip()
+#
+# services_prompt = str(_('Services to be enabled with this profile (space separated, leave blank to skip): '))
+# edit_services = ' '.join(editing.services) if editing else ''
+# services = menu.TextInput(services_prompt, edit_services).run().strip()
+#
+# choice = menu.Menu(
+# str(_('Should this profile be enabled for installation?')),
+# menu.Menu.yes_no(),
+# skip=False,
+# default_option=menu.Menu.no(),
+# clear_screen=False,
+# show_search_hint=False
+# ).run()
+#
+# enable_profile = True if choice.value == menu.Menu.yes() else False
+#
+# profile = CustomTypeProfile(
+# profile_name,
+# enabled=enable_profile,
+# packages=packages.split(' '),
+# services=services.split(' ')
+# )
+#
+# return profile
+#
+#
+# # TODO
+# # Still needs some ironing out
+# class CustomProfile():
+# def __init__(self):
+# super().__init__(
+# 'Custom',
+# ProfileType.Custom,
+# description=str(_('Create your own'))
+# )
+#
+# def json(self) -> Dict[str, Any]:
+# data: Dict[str, Any] = {'main': self.name, 'gfx_driver': self.gfx_driver, 'custom': []}
+#
+# for profile in self._current_selection:
+# data['custom'].append({
+# 'name': profile.name,
+# 'packages': profile.packages,
+# 'services': profile.services,
+# 'enabled': profile.custom_enabled
+# })
+#
+# return data
+#
+# def do_on_select(self) -> SelectResult:
+# custom_profile_list = CustomProfileList('', profile_handler.get_custom_profiles())
+# custom_profiles = custom_profile_list.run()
+#
+# # we'll first remove existing custom default_profiles with
+# # the same name and then add the new ones this
+# # will avoid errors of default_profiles with duplicate naming
+# profile_handler.remove_custom_profiles(custom_profiles)
+# profile_handler.add_custom_profiles(custom_profiles)
+#
+# self.set_current_selection(custom_profiles)
+#
+# if custom_profile_list.is_last_choice_cancel():
+# return SelectResult.SameSelection
+#
+# enabled_profiles = [p for p in self._current_selection if p.custom_enabled]
+# # in case we only created inactive default_profiles we wanna store them but
+# # we want to reset the original setting
+# if not enabled_profiles:
+# return SelectResult.ResetCurrent
+#
+# return SelectResult.NewSelection
+#
+# def post_install(self, install_session: 'Installer'):
+# for profile in self._current_selection:
+# profile.post_install(install_session)
+#
+# def install(self, install_session: 'Installer'):
+# driver_packages = self.gfx_driver_packages()
+# install_session.add_additional_packages(driver_packages)
+#
+# for profile in self._current_selection:
+# if profile.custom_enabled:
+# log(f'Installing custom profile {profile.name}...')
+#
+# install_session.add_additional_packages(profile.packages)
+# install_session.enable_service(profile.services)
+#
+# profile.install(install_session)
+#
+# def info(self) -> Optional[ProfileInfo]:
+# enabled_profiles = [p for p in self._current_selection if p.custom_enabled]
+# if enabled_profiles:
+# details = ', '.join([p.name for p in enabled_profiles])
+# gfx_driver = self.gfx_driver
+# return ProfileInfo(self.name, details, gfx_driver)
+#
+# return None
+#
+# def reset(self):
+# for profile in self._current_selection:
+# profile.set_enabled(False)
+#
+# self.gfx_driver = None
+#
+#
+# class CustomTypeProfile(Profile):
+# def __init__(
+# self,
+# name: str,
+# enabled: bool = False,
+# packages: List[str] = [],
+# services: List[str] = []
+# ):
+# super().__init__(
+# name,
+# ProfileType.CustomType,
+# packages=packages,
+# services=services,
+# support_gfx_driver=True
+# )
+#
+# self.custom_enabled = enabled
+#
+# def json(self) -> Dict[str, Any]:
+# return {
+# 'name': self.name,
+# 'packages': self.packages,
+# 'services': self.services,
+# 'enabled': self.custom_enabled
+# }
diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py
new file mode 100644
index 00000000..417d86d6
--- /dev/null
+++ b/archinstall/default_profiles/desktop.py
@@ -0,0 +1,87 @@
+from typing import Any, TYPE_CHECKING, List, Optional, Dict
+
+from archinstall.lib import menu
+from archinstall.lib.output import info
+from archinstall.lib.profile.profiles_handler import profile_handler
+from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, GreeterType
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class DesktopProfile(Profile):
+ def __init__(self, current_selection: List[Profile] = []):
+ super().__init__(
+ 'Desktop',
+ ProfileType.Desktop,
+ description=str(_('Provides a selection of desktop environments and tiling window managers, e.g. GNOME, KDE Plasma, Sway')),
+ current_selection=current_selection,
+ support_greeter=True
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ 'nano',
+ 'vim',
+ 'openssh',
+ 'htop',
+ 'wget',
+ 'iwd',
+ 'wireless_tools',
+ 'wpa_supplicant',
+ 'smartmontools',
+ 'xdg-utils'
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ combined_greeters: Dict[GreeterType, int] = {}
+ for profile in self.current_selection:
+ if profile.default_greeter_type:
+ combined_greeters.setdefault(profile.default_greeter_type, 0)
+ combined_greeters[profile.default_greeter_type] += 1
+
+ if len(combined_greeters) >= 1:
+ return list(combined_greeters)[0]
+
+ return None
+
+ def _do_on_select_profiles(self):
+ for profile in self.current_selection:
+ profile.do_on_select()
+
+ def do_on_select(self) -> SelectResult:
+ choice = profile_handler.select_profile(
+ profile_handler.get_desktop_profiles(),
+ self._current_selection,
+ title=str(_('Select your desired desktop environment')),
+ multi=True
+ )
+
+ match choice.type_:
+ case menu.MenuSelectionType.Selection:
+ self.set_current_selection(choice.value) # type: ignore
+ self._do_on_select_profiles()
+ return SelectResult.NewSelection
+ case menu.MenuSelectionType.Skip:
+ return SelectResult.SameSelection
+ case menu.MenuSelectionType.Reset:
+ return SelectResult.ResetCurrent
+
+ def post_install(self, install_session: 'Installer'):
+ for profile in self._current_selection:
+ profile.post_install(install_session)
+
+ def install(self, install_session: 'Installer'):
+ # Install common packages for all desktop environments
+ install_session.add_additional_packages(self.packages)
+
+ for profile in self._current_selection:
+ info(f'Installing profile {profile.name}...')
+
+ install_session.add_additional_packages(profile.packages)
+ install_session.enable_service(profile.services)
+
+ profile.install(install_session)
diff --git a/profiles/applications/__init__.py b/archinstall/default_profiles/desktops/__init__.py
index e69de29b..e69de29b 100644
--- a/profiles/applications/__init__.py
+++ b/archinstall/default_profiles/desktops/__init__.py
diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py
new file mode 100644
index 00000000..3833ce71
--- /dev/null
+++ b/archinstall/default_profiles/desktops/awesome.py
@@ -0,0 +1,63 @@
+from typing import List, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class AwesomeProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Awesome', ProfileType.WindowMgr, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return super().packages + [
+ 'awesome',
+ 'alacritty',
+ 'xorg-xinit',
+ 'xorg-xrandr',
+ 'xterm',
+ 'feh',
+ 'slock',
+ 'terminus-font',
+ 'gnu-free-fonts',
+ 'ttf-liberation',
+ 'xsel',
+ ]
+
+ def install(self, install_session: 'Installer'):
+ super().install(install_session)
+
+ # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead.
+ with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", 'r') as fh:
+ awesome_lua = fh.read()
+
+ # Replace xterm with alacritty for a smoother experience.
+ awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"')
+
+ with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", 'w') as fh:
+ fh.write(awesome_lua)
+
+ # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config)
+
+ # TODO: check if we selected a greeter,
+ # but for now, awesome is intended to run without one.
+ with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'r') as xinitrc:
+ xinitrc_data = xinitrc.read()
+
+ for line in xinitrc_data.split('\n'):
+ if "twm &" in line:
+ xinitrc_data = xinitrc_data.replace(line, f"# {line}")
+ if "xclock" in line:
+ xinitrc_data = xinitrc_data.replace(line, f"# {line}")
+ if "xterm" in line:
+ xinitrc_data = xinitrc_data.replace(line, f"# {line}")
+
+ xinitrc_data += '\n'
+ xinitrc_data += 'exec awesome\n'
+
+ with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'w') as xinitrc:
+ xinitrc.write(xinitrc_data) \ No newline at end of file
diff --git a/archinstall/default_profiles/desktops/bspwm.py b/archinstall/default_profiles/desktops/bspwm.py
new file mode 100644
index 00000000..61eeba43
--- /dev/null
+++ b/archinstall/default_profiles/desktops/bspwm.py
@@ -0,0 +1,27 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class BspwmProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Bspwm', ProfileType.WindowMgr, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ # return super().packages + [
+ return [
+ 'bspwm',
+ 'sxhkd',
+ 'dmenu',
+ 'xdo',
+ 'rxvt-unicode'
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py
new file mode 100644
index 00000000..28c05f45
--- /dev/null
+++ b/archinstall/default_profiles/desktops/budgie.py
@@ -0,0 +1,26 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class BudgieProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Budgie', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "arc-gtk-theme",
+ "budgie",
+ "mate-terminal",
+ "nemo",
+ "papirus-icon-theme"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.LightdmSlick
diff --git a/archinstall/default_profiles/desktops/cinnamon.py b/archinstall/default_profiles/desktops/cinnamon.py
new file mode 100644
index 00000000..a819b4d1
--- /dev/null
+++ b/archinstall/default_profiles/desktops/cinnamon.py
@@ -0,0 +1,27 @@
+from typing import Optional, List, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class CinnamonProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Cinnamon', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "cinnamon",
+ "system-config-printer",
+ "gnome-keyring",
+ "gnome-terminal",
+ "blueberry",
+ "metacity"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/cutefish.py b/archinstall/default_profiles/desktops/cutefish.py
new file mode 100644
index 00000000..c4202920
--- /dev/null
+++ b/archinstall/default_profiles/desktops/cutefish.py
@@ -0,0 +1,27 @@
+from typing import Optional, List, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class CutefishProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Cutefish', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "cutefish",
+ "noto-fonts"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Sddm
+
+ def install(self, install_session: 'Installer'):
+ super().install(install_session)
diff --git a/archinstall/default_profiles/desktops/deepin.py b/archinstall/default_profiles/desktops/deepin.py
new file mode 100644
index 00000000..e6a9f6b5
--- /dev/null
+++ b/archinstall/default_profiles/desktops/deepin.py
@@ -0,0 +1,24 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class DeepinProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Deepin', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "deepin",
+ "deepin-terminal",
+ "deepin-editor"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/enlightenment.py b/archinstall/default_profiles/desktops/enlightenment.py
new file mode 100644
index 00000000..7dd7822a
--- /dev/null
+++ b/archinstall/default_profiles/desktops/enlightenment.py
@@ -0,0 +1,23 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class EnlighenmentProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Enlightenment', ProfileType.WindowMgr, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "enlightenment",
+ "terminology"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/gnome.py b/archinstall/default_profiles/desktops/gnome.py
new file mode 100644
index 00000000..24ade437
--- /dev/null
+++ b/archinstall/default_profiles/desktops/gnome.py
@@ -0,0 +1,23 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class GnomeProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Gnome', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ 'gnome',
+ 'gnome-tweaks'
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Gdm
diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py
new file mode 100644
index 00000000..0c5452eb
--- /dev/null
+++ b/archinstall/default_profiles/desktops/hyprland.py
@@ -0,0 +1,68 @@
+from enum import Enum
+from typing import List, Optional, TYPE_CHECKING, Any
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+from archinstall.lib.menu import Menu
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class SeatAccess(Enum):
+ seatd = 'seatd'
+ polkit = 'polkit'
+
+
+class HyprlandProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Hyprland', ProfileType.DesktopEnv, description='')
+
+ self.custom_settings = {'seat_access': None}
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "hyprland",
+ "dunst",
+ "kitty",
+ "dolphin",
+ "wofi",
+ "xdg-desktop-portal-hyprland",
+ "qt5-wayland",
+ "qt6-wayland",
+ "polkit-kde-agent",
+ "grim",
+ "slurp"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Sddm
+
+ @property
+ def services(self) -> List[str]:
+ if pref := self.custom_settings.get('seat_access', None):
+ return [pref]
+ return []
+
+ def _ask_seat_access(self):
+ # need to activate seat service and add to seat group
+ title = str(_('Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)'))
+ title += str(_('\n\nChoose an option to give Hyprland access to your hardware'))
+
+ options = [e.value for e in SeatAccess]
+ default = None
+
+ if seat := self.custom_settings.get('seat_access', None):
+ default = seat
+
+ choice = Menu(title, options, skip=False, preset_values=default).run()
+ self.custom_settings['seat_access'] = choice.single_value
+
+ def do_on_select(self):
+ self._ask_seat_access()
+
+ def install(self, install_session: 'Installer'):
+ super().install(install_session)
diff --git a/archinstall/default_profiles/desktops/i3.py b/archinstall/default_profiles/desktops/i3.py
new file mode 100644
index 00000000..9c2994de
--- /dev/null
+++ b/archinstall/default_profiles/desktops/i3.py
@@ -0,0 +1,29 @@
+from typing import Optional, List, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class I3wmProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('i3-wm', ProfileType.WindowMgr, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ 'i3-wm',
+ 'i3lock',
+ 'i3status',
+ 'i3blocks',
+ 'xterm',
+ 'lightdm-gtk-greeter',
+ 'lightdm',
+ 'dmenu'
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/lxqt.py b/archinstall/default_profiles/desktops/lxqt.py
new file mode 100644
index 00000000..5d75e08d
--- /dev/null
+++ b/archinstall/default_profiles/desktops/lxqt.py
@@ -0,0 +1,31 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class LxqtProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Lxqt', ProfileType.DesktopEnv, description='')
+
+ # NOTE: SDDM is the only officially supported greeter for LXQt, so unlike other DEs, lightdm is not used here.
+ # LXQt works with lightdm, but since this is not supported, we will not default to this.
+ # https://github.com/lxqt/lxqt/issues/795
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "lxqt",
+ "breeze-icons",
+ "oxygen-icons",
+ "xdg-utils",
+ "ttf-freefont",
+ "leafpad",
+ "slock"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Sddm
diff --git a/archinstall/default_profiles/desktops/mate.py b/archinstall/default_profiles/desktops/mate.py
new file mode 100644
index 00000000..d3c4a6e1
--- /dev/null
+++ b/archinstall/default_profiles/desktops/mate.py
@@ -0,0 +1,23 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class MateProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Mate', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "mate",
+ "mate-extra"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/plasma.py b/archinstall/default_profiles/desktops/plasma.py
new file mode 100644
index 00000000..bcc1ea1b
--- /dev/null
+++ b/archinstall/default_profiles/desktops/plasma.py
@@ -0,0 +1,27 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+class PlasmaProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('KDE Plasma', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "plasma-meta",
+ "konsole",
+ "kwrite",
+ "dolphin",
+ "ark",
+ "plasma-workspace",
+ "egl-wayland"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Sddm
diff --git a/archinstall/default_profiles/desktops/qtile.py b/archinstall/default_profiles/desktops/qtile.py
new file mode 100644
index 00000000..96e93b1d
--- /dev/null
+++ b/archinstall/default_profiles/desktops/qtile.py
@@ -0,0 +1,23 @@
+from typing import Optional, List, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class QtileProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('Qtile', ProfileType.WindowMgr, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ 'qtile',
+ 'alacritty'
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py
new file mode 100644
index 00000000..c757797d
--- /dev/null
+++ b/archinstall/default_profiles/desktops/sway.py
@@ -0,0 +1,77 @@
+from enum import Enum
+from typing import List, Optional, TYPE_CHECKING, Any
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+from archinstall.lib.menu import Menu
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class SeatAccess(Enum):
+ seatd = 'seatd'
+ polkit = 'polkit'
+
+
+class SwayProfile(XorgProfile):
+ def __init__(self):
+ super().__init__(
+ 'Sway',
+ ProfileType.WindowMgr,
+ description=''
+ )
+
+ self.custom_settings = {'seat_access': None}
+
+ @property
+ def packages(self) -> List[str]:
+ additional = []
+ if seat := self.custom_settings.get('seat_access', None):
+ additional = [seat]
+
+ return [
+ "sway",
+ "swaybg",
+ "swaylock",
+ "swayidle",
+ "waybar",
+ "dmenu",
+ "brightnessctl",
+ "grim",
+ "slurp",
+ "pavucontrol",
+ "foot",
+ "xorg-xwayland"
+ ] + additional
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
+
+ @property
+ def services(self) -> List[str]:
+ if pref := self.custom_settings.get('seat_access', None):
+ return [pref]
+ return []
+
+ def _ask_seat_access(self):
+ # need to activate seat service and add to seat group
+ title = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)'))
+ title += str(_('\n\nChoose an option to give Sway access to your hardware'))
+
+ options = [e.value for e in SeatAccess]
+ default = None
+
+ if seat := self.custom_settings.get('seat_access', None):
+ default = seat
+
+ choice = Menu(title, options, skip=False, preset_values=default).run()
+ self.custom_settings['seat_access'] = choice.single_value
+
+ def do_on_select(self):
+ self._ask_seat_access()
+
+ def install(self, install_session: 'Installer'):
+ super().install(install_session)
diff --git a/archinstall/default_profiles/desktops/xfce4.py b/archinstall/default_profiles/desktops/xfce4.py
new file mode 100644
index 00000000..a7f0a7e6
--- /dev/null
+++ b/archinstall/default_profiles/desktops/xfce4.py
@@ -0,0 +1,26 @@
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType, GreeterType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class Xfce4Profile(XorgProfile):
+ def __init__(self):
+ super().__init__('Xfce4', ProfileType.DesktopEnv, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ "xfce4",
+ "xfce4-goodies",
+ "pavucontrol",
+ "gvfs",
+ "xarchiver"
+ ]
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ return GreeterType.Lightdm
diff --git a/archinstall/default_profiles/minimal.py b/archinstall/default_profiles/minimal.py
new file mode 100644
index 00000000..f78708e9
--- /dev/null
+++ b/archinstall/default_profiles/minimal.py
@@ -0,0 +1,15 @@
+from typing import Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class MinimalProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Minimal',
+ ProfileType.Minimal,
+ description=str(_('A very basic installation that allows you to customize Arch Linux as you see fit.'))
+ )
diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py
new file mode 100644
index 00000000..4c85b0c7
--- /dev/null
+++ b/archinstall/default_profiles/profile.py
@@ -0,0 +1,205 @@
+from __future__ import annotations
+
+from enum import Enum, auto
+from typing import List, Optional, Any, Dict, TYPE_CHECKING, TypeVar
+
+from archinstall.lib.utils.util import format_cols
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+TProfile = TypeVar('TProfile', bound='Profile')
+
+
+class ProfileType(Enum):
+ # top level default_profiles
+ Server = 'Server'
+ Desktop = 'Desktop'
+ Xorg = 'Xorg'
+ Minimal = 'Minimal'
+ Custom = 'Custom'
+ # detailed selection default_profiles
+ ServerType = 'ServerType'
+ WindowMgr = 'Window Manager'
+ DesktopEnv = 'Desktop Environment'
+ CustomType = 'CustomType'
+ # special things
+ Tailored = 'Tailored'
+ Application = 'Application'
+
+
+class GreeterType(Enum):
+ Lightdm = 'lightdm-gtk-greeter'
+ LightdmSlick = 'lightdm-slick-greeter'
+ Sddm = 'sddm'
+ Gdm = 'gdm'
+ Ly = 'ly'
+
+
+class SelectResult(Enum):
+ NewSelection = auto()
+ SameSelection = auto()
+ ResetCurrent = auto()
+
+
+class Profile:
+ def __init__(
+ self,
+ name: str,
+ profile_type: ProfileType,
+ description: str = '',
+ current_selection: List[TProfile] = [],
+ packages: List[str] = [],
+ services: List[str] = [],
+ support_gfx_driver: bool = False,
+ support_greeter: bool = False
+ ):
+ self.name = name
+ self.description = description
+ self.profile_type = profile_type
+ self.custom_settings: Dict[str, Any] = {}
+
+ self._support_gfx_driver = support_gfx_driver
+ self._support_greeter = support_greeter
+
+ # self.gfx_driver: Optional[str] = None
+
+ self._current_selection = current_selection
+ self._packages = packages
+ self._services = services
+
+ # Only used for custom default_profiles
+ self.custom_enabled = False
+
+ @property
+ def current_selection(self) -> List[TProfile]:
+ return self._current_selection
+
+ @property
+ def packages(self) -> List[str]:
+ """
+ Returns a list of packages that should be installed when
+ this profile is among the chosen ones
+ """
+ return self._packages
+
+ @property
+ def services(self) -> List[str]:
+ """
+ Returns a list of services that should be enabled when
+ this profile is among the chosen ones
+ """
+ return self._services
+
+ @property
+ def default_greeter_type(self) -> Optional[GreeterType]:
+ """
+ Setting a default greeter type for a desktop profile
+ """
+ return None
+
+ def install(self, install_session: 'Installer'):
+ """
+ Performs installation steps when this profile was selected
+ """
+
+ def post_install(self, install_session: 'Installer'):
+ """
+ Hook that will be called when the installation process is
+ finished and custom installation steps for specific default_profiles
+ are needed
+ """
+
+ def json(self) -> Dict:
+ """
+ Returns a json representation of the profile
+ """
+ return {}
+
+ def do_on_select(self) -> SelectResult:
+ """
+ Hook that will be called when a profile is selected
+ """
+ return SelectResult.NewSelection
+
+ def set_custom_settings(self, settings: Dict[str, Any]):
+ """
+ Set the custom settings for the profile.
+ This is also called when the settings are parsed from the config
+ and can be overridden to perform further actions based on the profile
+ """
+ self.custom_settings = settings
+
+ def current_selection_names(self) -> List[str]:
+ if self._current_selection:
+ return [s.name for s in self._current_selection]
+ return []
+
+ def reset(self):
+ self.set_current_selection([])
+
+ def set_current_selection(self, current_selection: List[TProfile]):
+ self._current_selection = current_selection
+
+ def is_top_level_profile(self) -> bool:
+ top_levels = [ProfileType.Desktop, ProfileType.Server, ProfileType.Xorg, ProfileType.Minimal, ProfileType.Custom]
+ return self.profile_type in top_levels
+
+ def is_desktop_profile(self) -> bool:
+ return self.profile_type == ProfileType.Desktop
+
+ def is_server_type_profile(self) -> bool:
+ return self.profile_type == ProfileType.ServerType
+
+ def is_desktop_type_profile(self) -> bool:
+ return self.profile_type == ProfileType.DesktopEnv or self.profile_type == ProfileType.WindowMgr
+
+ def is_xorg_type_profile(self) -> bool:
+ return self.profile_type == ProfileType.Xorg
+
+ def is_tailored(self) -> bool:
+ return self.profile_type == ProfileType.Tailored
+
+ def is_custom_type_profile(self) -> bool:
+ return self.profile_type == ProfileType.CustomType
+
+ def is_graphic_driver_supported(self) -> bool:
+ if not self._current_selection:
+ return self._support_gfx_driver
+ else:
+ if any([p._support_gfx_driver for p in self._current_selection]):
+ return True
+ return False
+
+ def is_greeter_supported(self) -> bool:
+ return self._support_greeter
+
+ def preview_text(self) -> Optional[str]:
+ """
+ Override this method to provide a preview text for the profile
+ """
+ return self.packages_text()
+
+ def packages_text(self, include_sub_packages: bool = False) -> Optional[str]:
+ header = str(_('Installed packages'))
+
+ text = ''
+ packages = []
+
+ if self.packages:
+ packages = self.packages
+
+ if include_sub_packages:
+ for p in self.current_selection:
+ if p.packages:
+ packages += p.packages
+
+ text += format_cols(sorted(set(packages)))
+
+ if text:
+ text = f'{header}: \n{text}'
+ return text
+
+ return None
diff --git a/archinstall/default_profiles/server.py b/archinstall/default_profiles/server.py
new file mode 100644
index 00000000..ab758975
--- /dev/null
+++ b/archinstall/default_profiles/server.py
@@ -0,0 +1,56 @@
+from typing import Any, TYPE_CHECKING, List
+
+from archinstall.lib.output import info
+from archinstall.lib.menu import MenuSelectionType
+from archinstall.lib.profile.profiles_handler import profile_handler
+from archinstall.default_profiles.profile import ProfileType, Profile, SelectResult, TProfile
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class ServerProfile(Profile):
+ def __init__(self, current_value: List[TProfile] = []):
+ super().__init__(
+ 'Server',
+ ProfileType.Server,
+ description=str(_('Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb')),
+ current_selection=current_value
+ )
+
+ def do_on_select(self) -> SelectResult:
+ available_servers = profile_handler.get_server_profiles()
+
+ choice = profile_handler.select_profile(
+ available_servers,
+ self._current_selection,
+ title=str(_('Choose which servers to install, if none then a minimal installation will be done')),
+ multi=True
+ )
+
+ match choice.type_:
+ case MenuSelectionType.Selection:
+ self.set_current_selection(choice.value) # type: ignore
+ return SelectResult.NewSelection
+ case MenuSelectionType.Skip:
+ return SelectResult.SameSelection
+ case MenuSelectionType.Reset:
+ return SelectResult.ResetCurrent
+
+ def post_install(self, install_session: 'Installer'):
+ for profile in self._current_selection:
+ profile.post_install(install_session)
+
+ def install(self, install_session: 'Installer'):
+ server_info = self.current_selection_names()
+ details = ', '.join(server_info)
+ info(f'Now installing the selected servers: {details}')
+
+ for server in self._current_selection:
+ info(f'Installing {server.name}...')
+ install_session.add_additional_packages(server.packages)
+ install_session.enable_service(server.services)
+ server.install(install_session)
+
+ info('If your selections included multiple servers with the same port, you may have to reconfigure them.')
diff --git a/archinstall/default_profiles/servers/__init__.py b/archinstall/default_profiles/servers/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/archinstall/default_profiles/servers/__init__.py
diff --git a/archinstall/default_profiles/servers/cockpit.py b/archinstall/default_profiles/servers/cockpit.py
new file mode 100644
index 00000000..8cac0976
--- /dev/null
+++ b/archinstall/default_profiles/servers/cockpit.py
@@ -0,0 +1,19 @@
+from typing import List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+
+class CockpitProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Cockpit',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['cockpit', 'udisks2', 'packagekit']
+
+ @property
+ def services(self) -> List[str]:
+ return ['cockpit.socket']
diff --git a/archinstall/default_profiles/servers/docker.py b/archinstall/default_profiles/servers/docker.py
new file mode 100644
index 00000000..f4800916
--- /dev/null
+++ b/archinstall/default_profiles/servers/docker.py
@@ -0,0 +1,33 @@
+from typing import List, Union, TYPE_CHECKING
+
+import archinstall
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+from archinstall.lib.models import User
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+
+
+class DockerProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Docker',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['docker']
+
+ @property
+ def services(self) -> List[str]:
+ return ['docker']
+
+ def post_install(self, install_session: 'Installer'):
+ users: Union[User, List[User]] = archinstall.arguments.get('!users', [])
+ if not isinstance(users, list):
+ users = [users]
+
+ for user in users:
+ install_session.arch_chroot(f'usermod -a -G docker {user.username}')
diff --git a/archinstall/default_profiles/servers/httpd.py b/archinstall/default_profiles/servers/httpd.py
new file mode 100644
index 00000000..595ce84f
--- /dev/null
+++ b/archinstall/default_profiles/servers/httpd.py
@@ -0,0 +1,19 @@
+from typing import List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+
+class HttpdProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'httpd',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['apache']
+
+ @property
+ def services(self) -> List[str]:
+ return ['httpd']
diff --git a/archinstall/default_profiles/servers/lighttpd.py b/archinstall/default_profiles/servers/lighttpd.py
new file mode 100644
index 00000000..00aa5564
--- /dev/null
+++ b/archinstall/default_profiles/servers/lighttpd.py
@@ -0,0 +1,19 @@
+from typing import List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+
+class LighttpdProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Lighttpd',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['lighttpd']
+
+ @property
+ def services(self) -> List[str]:
+ return ['lighttpd']
diff --git a/archinstall/default_profiles/servers/mariadb.py b/archinstall/default_profiles/servers/mariadb.py
new file mode 100644
index 00000000..4506f1bc
--- /dev/null
+++ b/archinstall/default_profiles/servers/mariadb.py
@@ -0,0 +1,25 @@
+from typing import List, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+
+
+class MariadbProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Mariadb',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['mariadb']
+
+ @property
+ def services(self) -> List[str]:
+ return ['mariadb']
+
+ def post_install(self, install_session: 'Installer'):
+ install_session.arch_chroot('mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql')
diff --git a/archinstall/default_profiles/servers/nginx.py b/archinstall/default_profiles/servers/nginx.py
new file mode 100644
index 00000000..6038616c
--- /dev/null
+++ b/archinstall/default_profiles/servers/nginx.py
@@ -0,0 +1,19 @@
+from typing import List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+
+class NginxProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Nginx',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['nginx']
+
+ @property
+ def services(self) -> List[str]:
+ return ['nginx']
diff --git a/archinstall/default_profiles/servers/postgresql.py b/archinstall/default_profiles/servers/postgresql.py
new file mode 100644
index 00000000..dba722ce
--- /dev/null
+++ b/archinstall/default_profiles/servers/postgresql.py
@@ -0,0 +1,26 @@
+from typing import List, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+
+
+class PostgresqlProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Postgresql',
+ ProfileType.ServerType,
+ ''
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['postgresql']
+
+ @property
+ def services(self) -> List[str]:
+ return ['postgresql']
+
+ def post_install(self, install_session: 'Installer'):
+ install_session.arch_chroot("initdb -D /var/lib/postgres/data", run_as='postgres')
diff --git a/archinstall/default_profiles/servers/sshd.py b/archinstall/default_profiles/servers/sshd.py
new file mode 100644
index 00000000..7f855b1a
--- /dev/null
+++ b/archinstall/default_profiles/servers/sshd.py
@@ -0,0 +1,19 @@
+from typing import List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+
+class SshdProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'sshd',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['openssh']
+
+ @property
+ def services(self) -> List[str]:
+ return ['sshd']
diff --git a/archinstall/default_profiles/servers/tomcat.py b/archinstall/default_profiles/servers/tomcat.py
new file mode 100644
index 00000000..9bd8837b
--- /dev/null
+++ b/archinstall/default_profiles/servers/tomcat.py
@@ -0,0 +1,19 @@
+from typing import List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+
+class TomcatProfile(Profile):
+ def __init__(self):
+ super().__init__(
+ 'Tomcat',
+ ProfileType.ServerType
+ )
+
+ @property
+ def packages(self) -> List[str]:
+ return ['tomcat10']
+
+ @property
+ def services(self) -> List[str]:
+ return ['tomcat10']
diff --git a/archinstall/default_profiles/tailored.py b/archinstall/default_profiles/tailored.py
new file mode 100644
index 00000000..62666249
--- /dev/null
+++ b/archinstall/default_profiles/tailored.py
@@ -0,0 +1,21 @@
+from typing import List, Any, TYPE_CHECKING
+
+from archinstall.default_profiles.profile import ProfileType
+from archinstall.default_profiles.xorg import XorgProfile
+
+if TYPE_CHECKING:
+ from archinstall.lib.installer import Installer
+ _: Any
+
+
+class TailoredProfile(XorgProfile):
+ def __init__(self):
+ super().__init__('52-54-00-12-34-56', ProfileType.Tailored, description='')
+
+ @property
+ def packages(self) -> List[str]:
+ return ['nano', 'wget', 'git']
+
+ def install(self, install_session: 'Installer'):
+ super().install(install_session)
+ # do whatever you like here :)
diff --git a/archinstall/default_profiles/xorg.py b/archinstall/default_profiles/xorg.py
new file mode 100644
index 00000000..88ba55a6
--- /dev/null
+++ b/archinstall/default_profiles/xorg.py
@@ -0,0 +1,34 @@
+from typing import Any, Optional, TYPE_CHECKING, List
+
+from archinstall.default_profiles.profile import Profile, ProfileType
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class XorgProfile(Profile):
+ def __init__(
+ self,
+ name: str = 'Xorg',
+ profile_type: ProfileType = ProfileType.Xorg,
+ description: str = str(_('Installs a minimal system as well as xorg and graphics drivers.')),
+ ):
+ super().__init__(
+ name,
+ profile_type,
+ description=description,
+ support_gfx_driver=True
+ )
+
+ def preview_text(self) -> Optional[str]:
+ text = str(_('Environment type: {}')).format(self.profile_type.value)
+ if packages := self.packages_text():
+ text += f'\n{packages}'
+
+ return text
+
+ @property
+ def packages(self) -> List[str]:
+ return [
+ 'xorg-server'
+ ]
diff --git a/archinstall/lib/systemd.py b/archinstall/lib/boot.py
index 64ffcae4..62c50df3 100644
--- a/archinstall/lib/systemd.py
+++ b/archinstall/lib/boot.py
@@ -1,58 +1,17 @@
-import logging
import time
-from typing import Iterator
+from typing import Iterator, Optional
from .exceptions import SysCallError
from .general import SysCommand, SysCommandWorker, locate_binary
from .installer import Installer
-from .output import log
+from .output import error
from .storage import storage
-class Ini:
- def __init__(self, *args :str, **kwargs :str):
- """
- Limited INI handler for now.
- Supports multiple keywords through dictionary list items.
- """
- self.kwargs = kwargs
-
- def __str__(self) -> str:
- result = ''
- first_row_done = False
- for top_level in self.kwargs:
- if first_row_done:
- result += f"\n[{top_level}]\n"
- else:
- result += f"[{top_level}]\n"
- first_row_done = True
-
- for key, val in self.kwargs[top_level].items():
- if type(val) == list:
- for item in val:
- result += f"{key}={item}\n"
- else:
- result += f"{key}={val}\n"
-
- return result
-
-
-class Systemd(Ini):
- """
- Placeholder class to do systemd specific setups.
- """
-
-
-class Networkd(Systemd):
- """
- Placeholder class to do systemd-network specific setups.
- """
-
-
class Boot:
def __init__(self, installation: Installer):
self.instance = installation
self.container_name = 'archinstall'
- self.session = None
+ self.session: Optional[SysCommandWorker] = None
self.ready = False
def __enter__(self) -> 'Boot':
@@ -63,17 +22,18 @@ class Boot:
self.session = existing_session.session
self.ready = existing_session.ready
else:
+ # '-P' or --console=pipe could help us not having to do a bunch
+ # of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual.
self.session = SysCommandWorker([
'/usr/bin/systemd-nspawn',
- '-D', self.instance.target,
+ '-D', str(self.instance.target),
'--timezone=off',
'-b',
'--no-pager',
'--machine', self.container_name
])
- # '-P' or --console=pipe could help us not having to do a bunch of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual.
- if not self.ready:
+ if not self.ready and self.session:
while self.session.is_alive():
if b' login:' in self.session:
self.ready = True
@@ -87,29 +47,37 @@ class Boot:
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
if len(args) >= 2 and args[1]:
- log(args[1], level=logging.ERROR, fg='red')
- log(f"The error above occurred in a temporary boot-up of the installation {self.instance}", level=logging.ERROR, fg="red")
+ error(
+ args[1],
+ f"The error above occurred in a temporary boot-up of the installation {self.instance}"
+ )
shutdown = None
- shutdown_exit_code = -1
+ shutdown_exit_code: Optional[int] = -1
try:
shutdown = SysCommand(f'systemd-run --machine={self.container_name} --pty shutdown now')
- except SysCallError as error:
- shutdown_exit_code = error.exit_code
+ except SysCallError as err:
+ shutdown_exit_code = err.exit_code
- while self.session.is_alive():
- time.sleep(0.25)
+ if self.session:
+ while self.session.is_alive():
+ time.sleep(0.25)
- if shutdown:
+ if shutdown and shutdown.exit_code:
shutdown_exit_code = shutdown.exit_code
- if self.session.exit_code == 0 or shutdown_exit_code == 0:
+ if self.session and (self.session.exit_code == 0 or shutdown_exit_code == 0):
storage['active_boot'] = None
else:
- raise SysCallError(f"Could not shut down temporary boot of {self.instance}: {self.session.exit_code}/{shutdown_exit_code}", exit_code=next(filter(bool, [self.session.exit_code, shutdown_exit_code])))
+ session_exit_code = self.session.exit_code if self.session else -1
+
+ raise SysCallError(
+ f"Could not shut down temporary boot of {self.instance}: {session_exit_code}/{shutdown_exit_code}",
+ exit_code=next(filter(bool, [session_exit_code, shutdown_exit_code]))
+ )
- def __iter__(self) -> Iterator[str]:
+ def __iter__(self) -> Iterator[bytes]:
if self.session:
for value in self.session:
yield value
diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py
index c036783f..95e237d7 100644
--- a/archinstall/lib/configuration.py
+++ b/archinstall/lib/configuration.py
@@ -1,28 +1,17 @@
import os
import json
import stat
-import logging
-import pathlib
-from typing import Optional, Dict
+import readline
+from pathlib import Path
+from typing import Optional, Dict, Any, TYPE_CHECKING
-from .hsm.fido import Fido2
-from .models.disk_encryption import DiskEncryption
+from .menu import Menu, MenuSelectionType
from .storage import storage
from .general import JSON, UNSAFE_JSON
-from .output import log
-from .exceptions import RequirementError
-
-
-def configuration_sanity_check():
- disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
- if disk_encryption is not None and disk_encryption.hsm_device:
- if not Fido2.get_fido2_devices():
- raise RequirementError(
- f"In order to use HSM to pair with the disk encryption,"
- + f" one needs to be accessible through /dev/hidraw* and support"
- + f" the FIDO2 protocol. You can check this by running"
- + f" 'systemd-cryptenroll --fido2-device=list'."
- )
+from .output import debug, info, warn
+
+if TYPE_CHECKING:
+ _: Any
class ConfigurationOutput:
@@ -35,15 +24,13 @@ class ConfigurationOutput:
:type config: Dict
"""
self._config = config
- self._user_credentials = {}
- self._disk_layout = None
- self._user_config = {}
- self._default_save_path = pathlib.Path(storage.get('LOG_PATH', '.'))
+ self._user_credentials: Dict[str, Any] = {}
+ self._user_config: Dict[str, Any] = {}
+ self._default_save_path = storage.get('LOG_PATH', Path('.'))
self._user_config_file = 'user_configuration.json'
self._user_creds_file = "user_credentials.json"
- self._disk_layout_file = "user_disk_layout.json"
- self._sensitive = ['!users']
+ self._sensitive = ['!users', '!root-password']
self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run']
self._process_config()
@@ -56,23 +43,18 @@ class ConfigurationOutput:
def user_configuration_file(self):
return self._user_config_file
- @property
- def disk_layout_file(self):
- return self._disk_layout_file
-
def _process_config(self):
- for key in self._config:
+ for key, value in self._config.items():
if key in self._sensitive:
- self._user_credentials[key] = self._config[key]
- elif key == 'disk_layouts':
- self._disk_layout = self._config[key]
+ self._user_credentials[key] = value
elif key in self._ignore:
pass
else:
- self._user_config[key] = self._config[key]
+ self._user_config[key] = value
- if key == 'disk_encryption' and self._config[key]: # special handling for encryption password
- self._user_credentials['encryption_password'] = self._config[key].encryption_password
+ # special handling for encryption password
+ if key == 'disk_encryption' and value:
+ self._user_credentials['encryption_password'] = value.encryption_password
def user_config_to_json(self) -> str:
return json.dumps({
@@ -81,11 +63,6 @@ class ConfigurationOutput:
'version': storage['__version__']
}, indent=4, sort_keys=True, cls=JSON)
- def disk_layout_to_json(self) -> Optional[str]:
- if self._disk_layout:
- return json.dumps(self._disk_layout, indent=4, sort_keys=True, cls=JSON)
- return None
-
def user_credentials_to_json(self) -> Optional[str]:
if self._user_credentials:
return json.dumps(self._user_credentials, indent=4, sort_keys=True, cls=UNSAFE_JSON)
@@ -93,60 +70,112 @@ class ConfigurationOutput:
def show(self):
print(_('\nThis is your chosen configuration:'))
- log(" -- Chosen configuration --", level=logging.DEBUG)
-
- user_conig = self.user_config_to_json()
- disk_layout = self.disk_layout_to_json()
- log(user_conig, level=logging.INFO)
-
- if disk_layout:
- log(disk_layout, level=logging.INFO)
+ debug(" -- Chosen configuration --")
+ info(self.user_config_to_json())
print()
- def _is_valid_path(self, dest_path :pathlib.Path) -> bool:
- if (not dest_path.exists()) or not (dest_path.is_dir()):
- log(
- 'Destination directory {} does not exist or is not a directory,\n Configuration files can not be saved'.format(dest_path.resolve()),
- fg="yellow"
+ 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 False
- return True
+ return dest_path_ok
- def save_user_config(self, dest_path :pathlib.Path = None):
+ def save_user_config(self, dest_path: Path):
if self._is_valid_path(dest_path):
target = dest_path / self._user_config_file
+ target.write_text(self.user_config_to_json())
+ os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
- with open(target, 'w') as config_file:
- config_file.write(self.user_config_to_json())
-
- os.chmod(str(dest_path / self._user_config_file), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
-
- def save_user_creds(self, dest_path :pathlib.Path = None):
+ def save_user_creds(self, dest_path: Path):
if self._is_valid_path(dest_path):
if user_creds := self.user_credentials_to_json():
target = dest_path / self._user_creds_file
+ target.write_text(user_creds)
+ os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
- with open(target, 'w') as config_file:
- config_file.write(user_creds)
-
- os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
+ def save(self, dest_path: Optional[Path] = None):
+ dest_path = dest_path or self._default_save_path
- def save_disk_layout(self, dest_path :pathlib.Path = None):
if self._is_valid_path(dest_path):
- if disk_layout := self.disk_layout_to_json():
- target = dest_path / self._disk_layout_file
-
- with target.open('w') as config_file:
- config_file.write(disk_layout)
+ self.save_user_config(dest_path)
+ self.save_user_creds(dest_path)
- os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
- def save(self, dest_path :pathlib.Path = None):
- if not dest_path:
- dest_path = self._default_save_path
+def save_config(config: Dict):
+ def preview(selection: str):
+ match options[selection]:
+ case "user_config":
+ serialized = config_output.user_config_to_json()
+ return f"{config_output.user_configuration_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}"
+ return str(_("No configuration"))
+ case "all":
+ output = [config_output.user_configuration_file]
+ if config_output.user_credentials_to_json():
+ output.append(config_output.user_credentials_file)
+ return '\n'.join(output)
+ return None
- if self._is_valid_path(dest_path):
- self.save_user_config(dest_path)
- self.save_user_creds(dest_path)
- self.save_disk_layout(dest_path)
+ try:
+ config_output = ConfigurationOutput(config)
+
+ options = {
+ str(_("Save user configuration (including disk layout)")): "user_config",
+ str(_("Save user credentials")): "user_creds",
+ str(_("Save all")): "all",
+ }
+
+ save_choice = Menu(
+ _("Choose which configuration to save"),
+ list(options),
+ sort=False,
+ skip=True,
+ preview_size=0.75,
+ preview_command=preview,
+ ).run()
+
+ if save_choice.type_ == MenuSelectionType.Skip:
+ return
+
+ readline.set_completer_delims("\t\n=")
+ readline.parse_and_bind("tab: complete")
+ while True:
+ path = input(
+ _(
+ "Enter a directory for the configuration(s) to be saved (tab completion enabled)\nSave directory: "
+ )
+ ).strip(" ")
+ dest_path = Path(path)
+ if dest_path.exists() and dest_path.is_dir():
+ break
+ info(_("Not a valid directory: {}").format(dest_path), fg="red")
+
+ if not path:
+ return
+
+ prompt = _(
+ "Do you want to save {} configuration file(s) in the following location?\n\n{}"
+ ).format(options[str(save_choice.value)], dest_path.absolute())
+
+ save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
+ if save_confirmation == Menu.no():
+ return
+
+ debug("Saving {} configuration files to {}".format(options[str(save_choice.value)], dest_path.absolute()))
+
+ match options[str(save_choice.value)]:
+ case "user_config":
+ config_output.save_user_config(dest_path)
+ case "user_creds":
+ config_output.save_user_creds(dest_path)
+ case "all":
+ config_output.save(dest_path)
+
+ except (KeyboardInterrupt, EOFError):
+ return
diff --git a/archinstall/lib/disk/__init__.py b/archinstall/lib/disk/__init__.py
index 352d04b9..7f881273 100644
--- a/archinstall/lib/disk/__init__.py
+++ b/archinstall/lib/disk/__init__.py
@@ -1,7 +1,48 @@
-from .btrfs import *
-from .helpers import *
-from .blockdevice import BlockDevice
-from .filesystem import Filesystem, MBR, GPT
-from .partition import *
-from .user_guides import *
-from .validators import * \ No newline at end of file
+from .device_handler import device_handler, disk_layouts
+from .fido import Fido2
+from .filesystem import FilesystemHandler
+from .subvolume_menu import SubvolumeMenu
+from .partitioning_menu import (
+ manual_partitioning,
+ PartitioningList
+)
+from .device_model import (
+ _DeviceInfo,
+ BDevice,
+ DiskLayoutType,
+ DiskLayoutConfiguration,
+ LvmLayoutType,
+ LvmConfiguration,
+ LvmVolumeGroup,
+ LvmVolume,
+ LvmVolumeStatus,
+ PartitionTable,
+ Unit,
+ Size,
+ SectorSize,
+ SubvolumeModification,
+ DeviceGeometry,
+ PartitionType,
+ PartitionFlag,
+ FilesystemType,
+ ModificationStatus,
+ PartitionModification,
+ DeviceModification,
+ EncryptionType,
+ DiskEncryption,
+ Fido2Device,
+ LsblkInfo,
+ CleanType,
+ get_lsblk_info,
+ get_all_lsblk_info,
+ get_lsblk_by_mountpoint,
+)
+from .encryption_menu import (
+ select_encryption_type,
+ select_encrypted_password,
+ select_hsm,
+ select_partitions_to_encrypt,
+ DiskEncryptionMenu,
+)
+
+from .disk_menu import DiskLayoutConfigurationMenu
diff --git a/archinstall/lib/disk/blockdevice.py b/archinstall/lib/disk/blockdevice.py
deleted file mode 100644
index 178b786a..00000000
--- a/archinstall/lib/disk/blockdevice.py
+++ /dev/null
@@ -1,301 +0,0 @@
-from __future__ import annotations
-import json
-import logging
-import time
-
-from collections import OrderedDict
-from dataclasses import dataclass
-from typing import Optional, Dict, Any, Iterator, List, TYPE_CHECKING
-
-from ..exceptions import DiskError, SysCallError
-from ..output import log
-from ..general import SysCommand
-from ..storage import storage
-
-
-if TYPE_CHECKING:
- from .partition import Partition
- _: Any
-
-
-@dataclass
-class BlockSizeInfo:
- start: str
- end: str
- size: str
-
-
-@dataclass
-class BlockInfo:
- pttype: str
- ptuuid: str
- size: int
- tran: Optional[str]
- rota: bool
- free_space: Optional[List[BlockSizeInfo]]
-
-
-class BlockDevice:
- def __init__(self, path :str, info :Optional[Dict[str, Any]] = None):
- if not info:
- from .helpers import all_blockdevices
- # If we don't give any information, we need to auto-fill it.
- # Otherwise any subsequent usage will break.
- self.info = all_blockdevices(partitions=False)[path].info
- else:
- self.info = info
-
- self._path = path
- self.keep_partitions = True
- self._block_info = self._fetch_information()
- self._partitions: Dict[str, 'Partition'] = {}
-
- self._load_partitions()
-
- # TODO: Currently disk encryption is a BIT misleading.
- # It's actually partition-encryption, but for future-proofing this
- # I'm placing the encryption password on a BlockDevice level.
-
- def __repr__(self, *args :str, **kwargs :str) -> str:
- return self._str_repr
-
- @property
- def path(self) -> str:
- return self._path
-
- @property
- def _str_repr(self) -> str:
- return f"BlockDevice({self._device_or_backfile}, size={self.size}GB, free_space={self._safe_free_space()}, bus_type={self.bus_type})"
-
- def as_json(self) -> Dict[str, Any]:
- return {
- str(_('Device')): self._device_or_backfile,
- str(_('Size')): f'{self.size}GB',
- str(_('Free space')): f'{self._safe_free_space()}',
- str(_('Bus-type')): f'{self.bus_type}'
- }
-
- def __iter__(self) -> Iterator['Partition']:
- for partition in self.partitions:
- yield self.partitions[partition]
-
- def __getitem__(self, key :str, *args :str, **kwargs :str) -> Any:
- if hasattr(self, key):
- return getattr(self, key)
-
- if self.info and key in self.info:
- return self.info[key]
-
- raise KeyError(f'{self.info} does not contain information: "{key}"')
-
- def __lt__(self, left_comparitor :'BlockDevice') -> bool:
- return self._path < left_comparitor.path
-
- def json(self) -> str:
- """
- json() has precedence over __dump__, so this is a way
- to give less/partial information for user readability.
- """
- return self._path
-
- def __dump__(self) -> Dict[str, Dict[str, Any]]:
- return {
- self._path: {
- 'partuuid': self.uuid,
- 'wipe': self.info.get('wipe', None),
- 'partitions': [part.__dump__() for part in self.partitions.values()]
- }
- }
-
- def _call_lsblk(self, path: str) -> Dict[str, Any]:
- output = SysCommand(f'lsblk --json -b -o+SIZE,PTTYPE,ROTA,TRAN,PTUUID {self._path}').decode('UTF-8')
- if output:
- lsblk_info = json.loads(output)
- return lsblk_info
-
- raise DiskError(f'Failed to read disk "{self.path}" with lsblk')
-
- def _load_partitions(self):
- from .partition import Partition
-
- self._partitions.clear()
-
- lsblk_info = self._call_lsblk(self._path)
- device = lsblk_info['blockdevices'][0]
- self._partitions.clear()
-
- if children := device.get('children', None):
- root = f'/dev/{device["name"]}'
- for child in children:
- part_id = child['name'].removeprefix(device['name'])
- self._partitions[part_id] = Partition(root + part_id, block_device=self, part_id=part_id)
-
- def _get_free_space(self) -> Optional[List[BlockSizeInfo]]:
- # NOTE: parted -s will default to `cancel` on prompt, skipping any partition
- # that is "outside" the disk. in /dev/sr0 this is usually the case with Archiso,
- # so the free will ignore the ESP partition and just give the "free" space.
- # Doesn't harm us, but worth noting in case something weird happens.
- try:
- output = SysCommand(f"parted -s --machine {self._path} print free").decode('utf-8')
- if output:
- free_lines = [line for line in output.split('\n') if 'free' in line]
- sizes = []
- for free_space in free_lines:
- _, start, end, size, *_ = free_space.strip('\r\n;').split(':')
- sizes.append(BlockSizeInfo(start, end, size))
-
- return sizes
- except SysCallError as error:
- log(f"Could not get free space on {self._path}: {error}", level=logging.DEBUG)
-
- return None
-
- def _fetch_information(self) -> BlockInfo:
- lsblk_info = self._call_lsblk(self._path)
- device = lsblk_info['blockdevices'][0]
- free_space = self._get_free_space()
-
- return BlockInfo(
- pttype=device['pttype'],
- ptuuid=device['ptuuid'],
- size=device['size'],
- tran=device['tran'],
- rota=device['rota'],
- free_space=free_space
- )
-
- @property
- def _device_or_backfile(self) -> Optional[str]:
- """
- Returns the actual device-endpoint of the BlockDevice.
- If it's a loop-back-device it returns the back-file,
- For other types it return self.device
- """
- if self.info.get('type') == 'loop':
- return self.info['back-file']
- else:
- return self.device
-
- @property
- def mountpoint(self) -> None:
- """
- A dummy function to enable transparent comparisons of mountpoints.
- As blockdevices can't be mounted directly, this will always be None
- """
- return None
-
- @property
- def device(self) -> Optional[str]:
- """
- Returns the device file of the BlockDevice.
- If it's a loop-back-device it returns the /dev/X device,
- If it's a ATA-drive it returns the /dev/X device
- And if it's a crypto-device it returns the parent device
- """
- if "DEVTYPE" not in self.info:
- raise DiskError(f'Could not locate backplane info for "{self._path}"')
-
- if self.info['DEVTYPE'] in ['disk','loop']:
- return self._path
- elif self.info['DEVTYPE'][:4] == 'raid':
- # This should catch /dev/md## raid devices
- return self._path
- elif self.info['DEVTYPE'] == 'crypt':
- if 'pkname' not in self.info:
- raise DiskError(f'A crypt device ({self._path}) without a parent kernel device name.')
- return f"/dev/{self.info['pkname']}"
- else:
- log(f"Unknown blockdevice type for {self._path}: {self.info['DEVTYPE']}", level=logging.DEBUG)
-
- return None
-
- @property
- def partition_type(self) -> str:
- return self._block_info.pttype
-
- @property
- def uuid(self) -> str:
- return self._block_info.ptuuid
-
- @property
- def size(self) -> float:
- from .helpers import convert_size_to_gb
- return convert_size_to_gb(self._block_info.size)
-
- @property
- def bus_type(self) -> Optional[str]:
- return self._block_info.tran
-
- @property
- def spinning(self) -> bool:
- return self._block_info.rota
-
- @property
- def partitions(self) -> Dict[str, 'Partition']:
- return OrderedDict(sorted(self._partitions.items()))
-
- @property
- def partition(self) -> List['Partition']:
- return list(self.partitions.values())
-
- @property
- def first_free_sector(self) -> str:
- if block_size := self._largest_free_space():
- return block_size.start
- else:
- return '512MB'
-
- @property
- def first_end_sector(self) -> str:
- if block_size := self._largest_free_space():
- return block_size.end
- else:
- return f"{self.size}GB"
-
- def _safe_free_space(self) -> str:
- if self._block_info.free_space:
- sizes = [free_space.size for free_space in self._block_info.free_space]
- return '+'.join(sizes)
- return '?'
-
- def _largest_free_space(self) -> Optional[BlockSizeInfo]:
- if self._block_info.free_space:
- sorted_sizes = sorted(self._block_info.free_space, key=lambda x: x.size, reverse=True)
- return sorted_sizes[0]
- return None
-
- def _partprobe(self) -> bool:
- return SysCommand(['partprobe', self._path]).exit_code == 0
-
- def flush_cache(self) -> None:
- self._load_partitions()
-
- def get_partition(self, uuid :Optional[str] = None, partuuid :Optional[str] = None) -> Partition:
- if not uuid and not partuuid:
- raise ValueError(f"BlockDevice.get_partition() requires either a UUID or a PARTUUID for lookups.")
-
- log(f"Retrieving partition PARTUUID={partuuid} or UUID={uuid}", level=logging.DEBUG, fg="gray")
-
- for count in range(storage.get('DISK_RETRY_ATTEMPTS', 5)):
- for partition_index, partition in self.partitions.items():
- try:
- if uuid and partition.uuid and partition.uuid.lower() == uuid.lower():
- log(f"Matched UUID={uuid} against {partition.uuid}", level=logging.DEBUG, fg="gray")
- return partition
- elif partuuid and partition.part_uuid and partition.part_uuid.lower() == partuuid.lower():
- log(f"Matched PARTUUID={partuuid} against {partition.part_uuid}", level=logging.DEBUG, fg="gray")
- return partition
- except DiskError as error:
- # Most likely a blockdevice that doesn't support or use UUID's
- # (like Microsoft recovery partition)
- log(f"Could not get UUID/PARTUUID of {partition}: {error}", level=logging.DEBUG, fg="gray")
- pass
-
- log(f"uuid {uuid} or {partuuid} not found. Waiting {storage.get('DISK_TIMEOUTS', 1) * count}s for next attempt",level=logging.DEBUG)
- self.flush_cache()
- time.sleep(storage.get('DISK_TIMEOUTS', 1) * count)
-
- log(f"Could not find {uuid}/{partuuid} in disk after 5 retries", level=logging.INFO)
- log(f"Cache: {self._partitions}")
- log(f"Partitions: {self.partitions.items()}")
- raise DiskError(f"Partition {uuid}/{partuuid} was never found on {self} despite several attempts.")
diff --git a/archinstall/lib/disk/btrfs/__init__.py b/archinstall/lib/disk/btrfs/__init__.py
deleted file mode 100644
index a26e0160..00000000
--- a/archinstall/lib/disk/btrfs/__init__.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from __future__ import annotations
-import pathlib
-import glob
-import logging
-from typing import Union, Dict, TYPE_CHECKING
-
-# https://stackoverflow.com/a/39757388/929999
-if TYPE_CHECKING:
- from ...installer import Installer
-
-from .btrfs_helpers import (
- subvolume_info_from_path as subvolume_info_from_path,
- find_parent_subvolume as find_parent_subvolume,
- setup_subvolumes as setup_subvolumes,
- mount_subvolume as mount_subvolume
-)
-from .btrfssubvolumeinfo import BtrfsSubvolumeInfo as BtrfsSubvolume
-from .btrfspartition import BTRFSPartition as BTRFSPartition
-
-from ...exceptions import DiskError, Deprecated
-from ...general import SysCommand
-from ...output import log
-
-
-def create_subvolume(installation: Installer, subvolume_location :Union[pathlib.Path, str]) -> bool:
- """
- This function uses btrfs to create a subvolume.
-
- @installation: archinstall.Installer instance
- @subvolume_location: a localized string or path inside the installation / or /boot for instance without specifying /mnt/boot
- """
-
- installation_mountpoint = installation.target
- if type(installation_mountpoint) == str:
- installation_mountpoint = pathlib.Path(installation_mountpoint)
- # Set up the required physical structure
- if type(subvolume_location) == str:
- subvolume_location = pathlib.Path(subvolume_location)
-
- target = installation_mountpoint / subvolume_location.relative_to(subvolume_location.anchor)
-
- # Difference from mount_subvolume:
- # We only check if the parent exists, since we'll run in to "target path already exists" otherwise
- if not target.parent.exists():
- target.parent.mkdir(parents=True)
-
- if glob.glob(str(target / '*')):
- raise DiskError(f"Cannot create subvolume at {target} because it contains data (non-empty folder target)")
-
- # Remove the target if it exists
- if target.exists():
- target.rmdir()
-
- log(f"Creating a subvolume on {target}", level=logging.INFO)
- if (cmd := SysCommand(f"btrfs subvolume create {target}")).exit_code != 0:
- raise DiskError(f"Could not create a subvolume at {target}: {cmd}")
diff --git a/archinstall/lib/disk/btrfs/btrfs_helpers.py b/archinstall/lib/disk/btrfs/btrfs_helpers.py
deleted file mode 100644
index f6d2734a..00000000
--- a/archinstall/lib/disk/btrfs/btrfs_helpers.py
+++ /dev/null
@@ -1,136 +0,0 @@
-import logging
-import re
-from pathlib import Path
-from typing import Optional, Dict, Any, TYPE_CHECKING
-
-from ...models.subvolume import Subvolume
-from ...exceptions import SysCallError, DiskError
-from ...general import SysCommand
-from ...output import log
-from ...plugins import plugins
-from ..helpers import get_mount_info
-from .btrfssubvolumeinfo import BtrfsSubvolumeInfo
-
-if TYPE_CHECKING:
- from .btrfspartition import BTRFSPartition
- from ...installer import Installer
-
-
-class fstab_btrfs_compression_plugin():
- def __init__(self, partition_dict):
- self.partition_dict = partition_dict
-
- def on_genfstab(self, installation):
- with open(f"{installation.target}/etc/fstab", 'r') as fh:
- fstab = fh.read()
-
- # Replace the {installation}/etc/fstab with entries
- # using the compress=zstd where the mountpoint has compression set.
- with open(f"{installation.target}/etc/fstab", 'w') as fh:
- for line in fstab.split('\n'):
- # So first we grab the mount options by using subvol=.*? as a locator.
- # And we also grab the mountpoint for the entry, for instance /var/log
- if (subvoldef := re.findall(',.*?subvol=.*?[\t ]', line)) and (mountpoint := re.findall('[\t ]/.*?[\t ]', line)):
- for subvolume in self.partition_dict.get('btrfs', {}).get('subvolumes', []):
- # We then locate the correct subvolume and check if it's compressed
- if subvolume.compress and subvolume.mountpoint == mountpoint[0].strip():
- # We then sneak in the compress=zstd option if it doesn't already exist:
- # We skip entries where compression is already defined
- if ',compress=zstd,' not in line:
- line = line.replace(subvoldef[0], f",compress=zstd{subvoldef[0]}")
- break
-
- fh.write(f"{line}\n")
-
- return True
-
-
-def mount_subvolume(installation: 'Installer', device: 'BTRFSPartition', subvolume: Subvolume):
- # we normalize the subvolume name (getting rid of slash at the start if exists.
- # In our implementation has no semantic load.
- # Every subvolume is created from the top of the hierarchy- and simplifies its further use
- name = subvolume.name.lstrip('/')
- mountpoint = Path(subvolume.mountpoint)
- installation_target = Path(installation.target)
-
- mountpoint = installation_target / mountpoint.relative_to(mountpoint.anchor)
- mountpoint.mkdir(parents=True, exist_ok=True)
- mount_options = subvolume.options + [f'subvol={name}']
-
- log(f"Mounting subvolume {name} on {device} to {mountpoint}", level=logging.INFO, fg="gray")
- SysCommand(f"mount {device.path} {mountpoint} -o {','.join(mount_options)}")
-
-
-def setup_subvolumes(installation: 'Installer', partition_dict: Dict[str, Any]):
- log(f"Setting up subvolumes: {partition_dict['btrfs']['subvolumes']}", level=logging.INFO, fg="gray")
-
- for subvolume in partition_dict['btrfs']['subvolumes']:
- # we normalize the subvolume name (getting rid of slash at the start if exists. In our implementation has no semantic load.
- # Every subvolume is created from the top of the hierarchy- and simplifies its further use
- name = subvolume.name.lstrip('/')
-
- # We create the subvolume using the BTRFSPartition instance.
- # That way we ensure not only easy access, but also accurate mount locations etc.
- partition_dict['device_instance'].create_subvolume(name, installation=installation)
-
- # Make the nodatacow processing now
- # It will be the main cause of creation of subvolumes which are not to be mounted
- # it is not an options which can be established by subvolume (but for whole file systems), and can be
- # set up via a simple attribute change in a directory (if empty). And here the directories are brand new
- if subvolume.nodatacow:
- if (cmd := SysCommand(f"chattr +C {installation.target}/{name}")).exit_code != 0:
- raise DiskError(f"Could not set nodatacow attribute at {installation.target}/{name}: {cmd}")
-
- # Make the compress processing now
- # it is not an options which can be established by subvolume (but for whole file systems), and can be
- # set up via a simple attribute change in a directory (if empty). And here the directories are brand new
- # in this way only zstd compression is activaded
- # TODO WARNING it is not clear if it should be a standard feature, so it might need to be deactivated
-
- if subvolume.compress:
- if not any(['compress' in filesystem_option for filesystem_option in partition_dict.get('filesystem', {}).get('mount_options', [])]):
- if (cmd := SysCommand(f"chattr +c {installation.target}/{name}")).exit_code != 0:
- raise DiskError(f"Could not set compress attribute at {installation.target}/{name}: {cmd}")
-
- if 'fstab_btrfs_compression_plugin' not in plugins:
- plugins['fstab_btrfs_compression_plugin'] = fstab_btrfs_compression_plugin(partition_dict)
-
-
-def subvolume_info_from_path(path: Path) -> Optional[BtrfsSubvolumeInfo]:
- try:
- subvolume_name = ''
- result = {}
- for index, line in enumerate(SysCommand(f"btrfs subvolume show {path}")):
- if index == 0:
- subvolume_name = line.strip().decode('UTF-8')
- continue
-
- if b':' in line:
- key, value = line.strip().decode('UTF-8').split(':', 1)
-
- # A bit of a hack, until I figure out how @dataclass
- # allows for hooking in a pre-processor to do this we have to do it here:
- result[key.lower().replace(' ', '_').replace('(s)', 's')] = value.strip()
-
- return BtrfsSubvolumeInfo(**{'full_path' : path, 'name' : subvolume_name, **result}) # type: ignore
- except SysCallError as error:
- log(f"Could not retrieve subvolume information from {path}: {error}", level=logging.WARNING, fg="orange")
-
- return None
-
-
-def find_parent_subvolume(path: Path, filters=[]) -> Optional[BtrfsSubvolumeInfo]:
- # A root path cannot have a parent
- if str(path) == '/':
- return None
-
- if found_mount := get_mount_info(str(path.parent), traverse=True, ignore=filters):
- if not (subvolume := subvolume_info_from_path(found_mount['target'])):
- if found_mount['target'] == '/':
- return None
-
- return find_parent_subvolume(path.parent, filters=[*filters, found_mount['target']])
-
- return subvolume
-
- return None
diff --git a/archinstall/lib/disk/btrfs/btrfspartition.py b/archinstall/lib/disk/btrfs/btrfspartition.py
deleted file mode 100644
index d04c9b98..00000000
--- a/archinstall/lib/disk/btrfs/btrfspartition.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import glob
-import pathlib
-import logging
-from typing import Optional, TYPE_CHECKING
-
-from ...exceptions import DiskError
-from ...storage import storage
-from ...output import log
-from ...general import SysCommand
-from ..partition import Partition
-from ..helpers import findmnt
-from .btrfs_helpers import (
- subvolume_info_from_path
-)
-
-if TYPE_CHECKING:
- from ...installer import Installer
- from .btrfssubvolumeinfo import BtrfsSubvolumeInfo
-
-
-class BTRFSPartition(Partition):
- def __init__(self, *args, **kwargs):
- Partition.__init__(self, *args, **kwargs)
-
- @property
- def subvolumes(self):
- for filesystem in findmnt(pathlib.Path(self.path), recurse=True).get('filesystems', []):
- if '[' in filesystem.get('source', ''):
- yield subvolume_info_from_path(filesystem['target'])
-
- def iterate_children(struct):
- for c in struct.get('children', []):
- if '[' in child.get('source', ''):
- yield subvolume_info_from_path(c['target'])
-
- for sub_child in iterate_children(c):
- yield sub_child
-
- for child in iterate_children(filesystem):
- yield child
-
- def create_subvolume(self, subvolume :pathlib.Path, installation :Optional['Installer'] = None) -> 'BtrfsSubvolumeInfo':
- """
- Subvolumes have to be created within a mountpoint.
- This means we need to get the current installation target.
- After we get it, we need to verify it is a btrfs subvolume filesystem.
- Finally, the destination must be empty.
- """
-
- # Allow users to override the installation session
- if not installation:
- installation = storage.get('installation_session')
-
- # Determain if the path given, is an absolute path or a relative path.
- # We do this by checking if the path contains a known mountpoint.
- if str(subvolume)[0] == '/':
- if filesystems := findmnt(subvolume, traverse=True).get('filesystems'):
- if (target := filesystems[0].get('target')) and target != '/' and str(subvolume).startswith(target):
- # Path starts with a known mountpoint which isn't /
- # Which means it's an absolute path to a mounted location.
- pass
- else:
- # Since it's not an absolute position with a known start.
- # We omit the anchor ('/' basically) and make sure it's appendable
- # to the installation.target later
- subvolume = subvolume.relative_to(subvolume.anchor)
- # else: We don't need to do anything about relative paths, they should be appendable to installation.target as-is.
-
- # If the subvolume is not absolute, then we do two checks:
- # 1. Check if the partition itself is mounted somewhere, and use that as a root
- # 2. Use an active Installer().target as the root, assuming it's filesystem is btrfs
- # If both above fail, we need to warn the user that such setup is not supported.
- if str(subvolume)[0] != '/':
- if self.mountpoint is None and installation is None:
- raise DiskError("When creating a subvolume on BTRFSPartition()'s, you need to either initiate a archinstall.Installer() or give absolute paths when creating the subvoulme.")
- elif self.mountpoint:
- subvolume = self.mountpoint / subvolume
- elif installation:
- ongoing_installation_destination = installation.target
- if type(ongoing_installation_destination) == str:
- ongoing_installation_destination = pathlib.Path(ongoing_installation_destination)
-
- subvolume = ongoing_installation_destination / subvolume
-
- subvolume.parent.mkdir(parents=True, exist_ok=True)
-
- # <!--
- # We perform one more check from the given absolute position.
- # And we traverse backwards in order to locate any if possible subvolumes above
- # our new btrfs subvolume. This is because it needs to be mounted under it to properly
- # function.
- # if btrfs_parent := find_parent_subvolume(subvolume):
- # print('Found parent:', btrfs_parent)
- # -->
-
- log(f'Attempting to create subvolume at {subvolume}', level=logging.DEBUG, fg="grey")
-
- if glob.glob(str(subvolume / '*')):
- raise DiskError(f"Cannot create subvolume at {subvolume} because it contains data (non-empty folder target is not supported by BTRFS)")
- # Ideally we would like to check if the destination is already a subvolume.
- # But then we would need the mount-point at this stage as well.
- # So we'll comment out this check:
- # elif subvolinfo := subvolume_info_from_path(subvolume):
- # raise DiskError(f"Destination {subvolume} is already a subvolume: {subvolinfo}")
-
- # And deal with it here:
- SysCommand(f"btrfs subvolume create {subvolume}")
-
- return subvolume_info_from_path(subvolume)
diff --git a/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py b/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py
deleted file mode 100644
index 5f5bdea6..00000000
--- a/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py
+++ /dev/null
@@ -1,192 +0,0 @@
-import pathlib
-import datetime
-import logging
-import string
-import random
-import shutil
-from dataclasses import dataclass
-from typing import Optional, List# , TYPE_CHECKING
-from functools import cached_property
-
-# if TYPE_CHECKING:
-# from ..blockdevice import BlockDevice
-
-from ...exceptions import DiskError
-from ...general import SysCommand
-from ...output import log
-from ...storage import storage
-
-
-@dataclass
-class BtrfsSubvolumeInfo:
- full_path :pathlib.Path
- name :str
- uuid :str
- parent_uuid :str
- creation_time :datetime.datetime
- subvolume_id :int
- generation :int
- gen_at_creation :int
- parent_id :int
- top_level_id :int
- send_transid :int
- send_time :datetime.datetime
- receive_transid :int
- received_uuid :Optional[str] = None
- flags :Optional[str] = None
- receive_time :Optional[datetime.datetime] = None
- snapshots :Optional[List] = None
-
- def __post_init__(self):
- self.full_path = pathlib.Path(self.full_path)
-
- # Convert "-" entries to `None`
- if self.parent_uuid == "-":
- self.parent_uuid = None
- if self.received_uuid == "-":
- self.received_uuid = None
- if self.flags == "-":
- self.flags = None
- if self.receive_time == "-":
- self.receive_time = None
- if self.snapshots == "":
- self.snapshots = []
-
- # Convert timestamps into datetime workable objects (and preserve timezone by using ISO formats)
- self.creation_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.creation_time))
- self.send_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.send_time))
- if self.receive_time:
- self.receive_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.receive_time))
-
- @property
- def parent_subvolume(self):
- from .btrfs_helpers import find_parent_subvolume
-
- return find_parent_subvolume(self.full_path)
-
- @property
- def root(self) -> bool:
- from .btrfs_helpers import subvolume_info_from_path
-
- # TODO: Make this function traverse storage['MOUNT_POINT'] and find the first
- # occurrence of a mountpoint that is a btrfs volume instead of lazy assume / is a subvolume.
- # It would also be nice if it could use findmnt(self.full_path) and traverse backwards
- # finding the last occurrence of a subvolume which 'self' belongs to.
- if volume := subvolume_info_from_path(storage['MOUNT_POINT']):
- return self.full_path == volume.full_path
-
- return False
-
- @cached_property
- def partition(self):
- from ..helpers import findmnt, get_parent_of_partition, all_blockdevices
- from ..partition import Partition
- from ..blockdevice import BlockDevice
- from ..mapperdev import MapperDev
- from .btrfspartition import BTRFSPartition
- from .btrfs_helpers import subvolume_info_from_path
-
- try:
- # If the subvolume is mounted, it's pretty trivial to lookup the partition (parent) device.
- if filesystem := findmnt(self.full_path).get('filesystems', []):
- if source := filesystem[0].get('source', None):
- # Strip away subvolume definitions from findmnt
- if '[' in source:
- source = source[:source.find('[')]
-
- if filesystem[0].get('fstype', '') == 'btrfs':
- return BTRFSPartition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source))))
- elif filesystem[0].get('source', '').startswith('/dev/mapper'):
- return MapperDev(source)
- else:
- return Partition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source))))
- except DiskError:
- # Subvolume has never been mounted, we have no reliable way of finding where it is.
- # But we have the UUID of the partition, and can begin looking for it by mounting
- # all blockdevices that we can reliably support.. This is taxing tho and won't cover all devices.
-
- log(f"Looking up {self}, this might take time.", fg="orange", level=logging.WARNING)
- for blockdevice, instance in all_blockdevices(mappers=True, partitions=True, error=True).items():
- if type(instance) in (Partition, MapperDev):
- we_mounted_it = False
- detection_mountpoint = instance.mountpoint
- if not detection_mountpoint:
- if type(instance) == Partition and instance.encrypted:
- # TODO: Perhaps support unlocking encrypted volumes?
- # This will cause a lot of potential user interactions tho.
- log(f"Ignoring {blockdevice} because it's encrypted.", fg="gray", level=logging.DEBUG)
- continue
-
- detection_mountpoint = pathlib.Path(f"/tmp/{''.join([random.choice(string.ascii_letters) for x in range(20)])}")
- detection_mountpoint.mkdir(parents=True, exist_ok=True)
-
- instance.mount(str(detection_mountpoint))
- we_mounted_it = True
-
- if (filesystem := findmnt(detection_mountpoint)) and (filesystem := filesystem.get('filesystems', [])):
- if subvolume := subvolume_info_from_path(filesystem[0]['target']):
- if subvolume.uuid == self.uuid:
- # The top level subvolume matched of ourselves,
- # which means the instance we're iterating has the subvol we're looking for.
- log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray")
- return instance
-
- def iterate_children(struct):
- for child in struct.get('children', []):
- if '[' in child.get('source', ''):
- yield subvolume_info_from_path(child['target'])
-
- for sub_child in iterate_children(child):
- yield sub_child
-
- for child in iterate_children(filesystem[0]):
- if child.uuid == self.uuid:
- # We found a child within the instance that has the subvol we're looking for.
- log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray")
- return instance
-
- if we_mounted_it:
- instance.unmount()
- shutil.rmtree(detection_mountpoint)
-
- @cached_property
- def mount_options(self) -> Optional[List[str]]:
- from ..helpers import findmnt
-
- if filesystem := findmnt(self.full_path).get('filesystems', []):
- return filesystem[0].get('options').split(',')
-
- def convert_to_ISO_format(self, time_string):
- time_string_almost_done = time_string.replace(' ', 'T', 1).replace(' ', '')
- iso_string = f"{time_string_almost_done[:-2]}:{time_string_almost_done[-2:]}"
- return iso_string
-
- def mount(self, mountpoint :pathlib.Path, options=None, include_previously_known_options=True):
- from ..helpers import findmnt
-
- try:
- if mnt_info := findmnt(pathlib.Path(mountpoint), traverse=False):
- log(f"Unmounting {mountpoint} as it was already mounted using {mnt_info}")
- SysCommand(f"umount {mountpoint}")
- except DiskError:
- # No previously mounted device at the mountpoint
- pass
-
- if not options:
- options = []
-
- try:
- if include_previously_known_options and (cached_options := self.mount_options):
- options += cached_options
- except DiskError:
- pass
-
- if not any('subvol=' in x for x in options):
- options += f'subvol={self.name}'
-
- SysCommand(f"mount {self.partition.path} {mountpoint} -o {','.join(options)}")
- log(f"{self} has successfully been mounted to {mountpoint}", level=logging.INFO, fg="gray")
-
- def unmount(self, recurse :bool = True):
- SysCommand(f"umount {'-R' if recurse else ''} {self.full_path}")
- log(f"Successfully unmounted {self}", level=logging.INFO, fg="gray")
diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py
new file mode 100644
index 00000000..7ba70382
--- /dev/null
+++ b/archinstall/lib/disk/device_handler.py
@@ -0,0 +1,809 @@
+from __future__ import annotations
+
+import json
+import os
+import logging
+import time
+from pathlib import Path
+from typing import List, Dict, Any, Optional, TYPE_CHECKING, Literal, Iterable
+
+from parted import ( # type: ignore
+ Disk, Geometry, FileSystem,
+ PartitionException, DiskLabelException,
+ getDevice, getAllDevices, freshDisk, Partition, Device
+)
+
+from .device_model import (
+ DeviceModification, PartitionModification,
+ BDevice, _DeviceInfo, _PartitionInfo,
+ FilesystemType, Unit, PartitionTable,
+ ModificationStatus, get_lsblk_info, LsblkInfo,
+ _BtrfsSubvolumeInfo, get_all_lsblk_info, DiskEncryption, LvmVolumeGroup, LvmVolume, Size, LvmGroupInfo,
+ SectorSize, LvmVolumeInfo, LvmPVInfo, SubvolumeModification, BtrfsMountOption
+)
+
+from ..exceptions import DiskError, UnknownFilesystemFormat
+from ..general import SysCommand, SysCallError, JSON, SysCommandWorker
+from ..luks import Luks2
+from ..output import debug, error, info, warn, log
+from ..utils.util import is_subpath
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class DeviceHandler(object):
+ _TMP_BTRFS_MOUNT = Path('/mnt/arch_btrfs')
+
+ def __init__(self):
+ self._devices: Dict[Path, BDevice] = {}
+ self.load_devices()
+
+ @property
+ def devices(self) -> List[BDevice]:
+ return list(self._devices.values())
+
+ def load_devices(self):
+ block_devices = {}
+
+ devices = getAllDevices()
+
+ try:
+ loop_devices = SysCommand(['losetup', '-a'])
+ for ld_info in str(loop_devices).splitlines():
+ loop_device = getDevice(ld_info.split(':', maxsplit=1)[0])
+ devices.append(loop_device)
+ except Exception as err:
+ debug(f'Failed to get loop devices: {err}')
+
+ for device in devices:
+ if get_lsblk_info(device.path).type == 'rom':
+ continue
+
+ try:
+ disk = Disk(device)
+ except DiskLabelException as err:
+ if 'unrecognised disk label' in getattr(error, 'message', str(err)):
+ disk = freshDisk(device, PartitionTable.GPT.value)
+ else:
+ debug(f'Unable to get disk from device: {device}')
+ continue
+
+ device_info = _DeviceInfo.from_disk(disk)
+ partition_infos = []
+
+ for partition in disk.partitions:
+ lsblk_info = get_lsblk_info(partition.path)
+ fs_type = self._determine_fs_type(partition, lsblk_info)
+ subvol_infos = []
+
+ if fs_type == FilesystemType.Btrfs:
+ subvol_infos = self.get_btrfs_info(partition.path)
+
+ partition_infos.append(
+ _PartitionInfo.from_partition(
+ partition,
+ fs_type,
+ lsblk_info.partn,
+ lsblk_info.partuuid,
+ lsblk_info.uuid,
+ lsblk_info.mountpoints,
+ subvol_infos
+ )
+ )
+
+ block_device = BDevice(disk, device_info, partition_infos)
+ block_devices[block_device.device_info.path] = block_device
+
+ self._devices = block_devices
+
+ def _determine_fs_type(
+ self,
+ partition: Partition,
+ lsblk_info: Optional[LsblkInfo] = None
+ ) -> Optional[FilesystemType]:
+ try:
+ if partition.fileSystem:
+ return FilesystemType(partition.fileSystem.type)
+ elif lsblk_info is not None:
+ return FilesystemType(lsblk_info.fstype) if lsblk_info.fstype else None
+ return None
+ except ValueError:
+ debug(f'Could not determine the filesystem: {partition.fileSystem}')
+
+ return None
+
+ def get_device(self, path: Path) -> Optional[BDevice]:
+ return self._devices.get(path, None)
+
+ def get_device_by_partition_path(self, partition_path: Path) -> Optional[BDevice]:
+ partition = self.find_partition(partition_path)
+ if partition:
+ device: Device = partition.disk.device
+ return self.get_device(Path(device.path))
+ return None
+
+ def find_partition(self, path: Path) -> Optional[_PartitionInfo]:
+ for device in self._devices.values():
+ part = next(filter(lambda x: str(x.path) == str(path), device.partition_infos), None)
+ if part is not None:
+ return part
+ return None
+
+ def get_parent_device_path(self, dev_path: Path) -> Path:
+ lsblk = get_lsblk_info(dev_path)
+ return Path(f'/dev/{lsblk.pkname}')
+
+ def get_unique_path_for_device(self, dev_path: Path) -> Optional[Path]:
+ paths = Path('/dev/disk/by-id').glob('*')
+ linked_targets = {p.resolve(): p for p in paths}
+ linked_wwn_targets = {p: linked_targets[p] for p in linked_targets
+ if p.name.startswith('wwn-') or p.name.startswith('nvme-eui.')}
+
+ if dev_path in linked_wwn_targets:
+ return linked_wwn_targets[dev_path]
+
+ if dev_path in linked_targets:
+ return linked_targets[dev_path]
+
+ return None
+
+ def get_uuid_for_path(self, path: Path) -> Optional[str]:
+ partition = self.find_partition(path)
+ return partition.partuuid if partition else None
+
+ def get_btrfs_info(self, dev_path: Path) -> List[_BtrfsSubvolumeInfo]:
+ lsblk_info = get_lsblk_info(dev_path)
+ subvol_infos: List[_BtrfsSubvolumeInfo] = []
+
+ if not lsblk_info.mountpoint:
+ self.mount(dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True)
+ mountpoint = self._TMP_BTRFS_MOUNT
+ else:
+ # when multiple subvolumes are mounted then the lsblk output may look like
+ # "mountpoint": "/mnt/archinstall/.snapshots"
+ # "mountpoints": ["/mnt/archinstall/.snapshots", "/mnt/archinstall/home", ..]
+ # so we'll determine the minimum common path and assume that's the root
+ path_strings = [str(m) for m in lsblk_info.mountpoints]
+ common_prefix = os.path.commonprefix(path_strings)
+ mountpoint = Path(common_prefix)
+
+ try:
+ result = SysCommand(f'btrfs subvolume list {mountpoint}').decode()
+ except SysCallError as err:
+ debug(f'Failed to read btrfs subvolume information: {err}')
+ return subvol_infos
+
+ try:
+ # ID 256 gen 16 top level 5 path @
+ for line in result.splitlines():
+ # expected output format:
+ # ID 257 gen 8 top level 5 path @home
+ name = Path(line.split(' ')[-1])
+ sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None)
+ subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint))
+ except json.decoder.JSONDecodeError as err:
+ error(f"Could not decode lsblk JSON: {result}")
+ raise err
+
+ if not lsblk_info.mountpoint:
+ self.umount(dev_path)
+
+ return subvol_infos
+
+ def format(
+ self,
+ fs_type: FilesystemType,
+ path: Path,
+ additional_parted_options: List[str] = []
+ ):
+ options = []
+ command = ''
+
+ match fs_type:
+ case FilesystemType.Btrfs:
+ options += ['-f']
+ command += 'mkfs.btrfs'
+ case FilesystemType.Fat16:
+ options += ['-F16']
+ command += 'mkfs.fat'
+ case FilesystemType.Fat32:
+ options += ['-F32']
+ command += 'mkfs.fat'
+ case FilesystemType.Ext2:
+ options += ['-F']
+ command += 'mkfs.ext2'
+ case FilesystemType.Ext3:
+ options += ['-F']
+ command += 'mkfs.ext3'
+ case FilesystemType.Ext4:
+ options += ['-F']
+ command += 'mkfs.ext4'
+ case FilesystemType.Xfs:
+ options += ['-f']
+ command += 'mkfs.xfs'
+ case FilesystemType.F2fs:
+ options += ['-f']
+ command += 'mkfs.f2fs'
+ case FilesystemType.Ntfs:
+ options += ['-f', '-Q']
+ command += 'mkfs.ntfs'
+ case FilesystemType.Reiserfs:
+ command += 'mkfs.reiserfs'
+ case _:
+ raise UnknownFilesystemFormat(f'Filetype "{fs_type.value}" is not supported')
+
+ options += additional_parted_options
+ options_str = ' '.join(options)
+
+ debug(f'Formatting filesystem: /usr/bin/{command} {options_str} {path}')
+
+ try:
+ SysCommand(f"/usr/bin/{command} {options_str} {path}")
+ except SysCallError as err:
+ msg = f'Could not format {path} with {fs_type.value}: {err.message}'
+ error(msg)
+ raise DiskError(msg) from err
+
+ def encrypt(
+ self,
+ dev_path: Path,
+ mapper_name: Optional[str],
+ enc_password: str,
+ lock_after_create: bool = True
+ ) -> Luks2:
+ luks_handler = Luks2(
+ dev_path,
+ mapper_name=mapper_name,
+ password=enc_password
+ )
+
+ key_file = luks_handler.encrypt()
+
+ luks_handler.unlock(key_file=key_file)
+
+ if not luks_handler.mapper_dev:
+ raise DiskError('Failed to unlock luks device')
+
+ if lock_after_create:
+ debug(f'luks2 locking device: {dev_path}')
+ luks_handler.lock()
+
+ return luks_handler
+
+ def format_encrypted(
+ self,
+ dev_path: Path,
+ mapper_name: Optional[str],
+ fs_type: FilesystemType,
+ enc_conf: DiskEncryption
+ ):
+ luks_handler = Luks2(
+ dev_path,
+ mapper_name=mapper_name,
+ password=enc_conf.encryption_password
+ )
+
+ key_file = luks_handler.encrypt()
+
+ luks_handler.unlock(key_file=key_file)
+
+ if not luks_handler.mapper_dev:
+ raise DiskError('Failed to unlock luks device')
+
+ info(f'luks2 formatting mapper dev: {luks_handler.mapper_dev}')
+ self.format(fs_type, luks_handler.mapper_dev)
+
+ info(f'luks2 locking device: {dev_path}')
+ luks_handler.lock()
+
+ def _lvm_info(
+ self,
+ cmd: str,
+ info_type: Literal['lv', 'vg', 'pvseg']
+ ) -> Optional[Any]:
+ raw_info = SysCommand(cmd).decode().split('\n')
+
+ # for whatever reason the output sometimes contains
+ # "File descriptor X leaked leaked on vgs invocation
+ data = '\n'.join([raw for raw in raw_info if 'File descriptor' not in raw])
+
+ debug(f'LVM info: {data}')
+
+ reports = json.loads(data)
+
+ for report in reports['report']:
+ if len(report[info_type]) != 1:
+ raise ValueError(f'Report does not contain any entry')
+
+ entry = report[info_type][0]
+
+ match info_type:
+ case 'pvseg':
+ return LvmPVInfo(
+ pv_name=Path(entry['pv_name']),
+ lv_name=entry['lv_name'],
+ vg_name=entry['vg_name'],
+ )
+ case 'lv':
+ return LvmVolumeInfo(
+ lv_name=entry['lv_name'],
+ vg_name=entry['vg_name'],
+ lv_size=Size(int(entry[f'lv_size'][:-1]), Unit.B, SectorSize.default())
+ )
+ case 'vg':
+ return LvmGroupInfo(
+ vg_uuid=entry['vg_uuid'],
+ vg_size=Size(int(entry[f'vg_size'][:-1]), Unit.B, SectorSize.default())
+ )
+
+ return None
+
+ def _lvm_info_with_retry(self, cmd: str, info_type: Literal['lv', 'vg', 'pvseg']) -> Optional[Any]:
+ attempts = 3
+
+ for attempt_nr in range(attempts):
+ try:
+ return self._lvm_info(cmd, info_type)
+ except ValueError:
+ time.sleep(attempt_nr + 1)
+
+ raise ValueError(f'Failed to fetch {info_type} information')
+
+ def lvm_vol_info(self, lv_name: str) -> Optional[LvmVolumeInfo]:
+ cmd = (
+ 'lvs --reportformat json '
+ '--unit B '
+ f'-S lv_name={lv_name}'
+ )
+
+ return self._lvm_info_with_retry(cmd, 'lv')
+
+ def lvm_group_info(self, vg_name: str) -> Optional[LvmGroupInfo]:
+ cmd = (
+ 'vgs --reportformat json '
+ '--unit B '
+ '-o vg_name,vg_uuid,vg_size '
+ f'-S vg_name={vg_name}'
+ )
+
+ return self._lvm_info_with_retry(cmd, 'vg')
+
+ def lvm_pvseg_info(self, vg_name: str, lv_name: str) -> Optional[LvmPVInfo]:
+ cmd = (
+ 'pvs '
+ '--segments -o+lv_name,vg_name '
+ f'-S vg_name={vg_name},lv_name={lv_name} '
+ '--reportformat json '
+ )
+
+ return self._lvm_info_with_retry(cmd, 'pvseg')
+
+ def lvm_vol_change(self, vol: LvmVolume, activate: bool):
+ active_flag = 'y' if activate else 'n'
+ cmd = f'lvchange -a {active_flag} {vol.safe_dev_path}'
+
+ debug(f'lvchange volume: {cmd}')
+ SysCommand(cmd)
+
+ def lvm_export_vg(self, vg: LvmVolumeGroup):
+ cmd = f'vgexport {vg.name}'
+
+ debug(f'vgexport: {cmd}')
+ SysCommand(cmd)
+
+ def lvm_import_vg(self, vg: LvmVolumeGroup):
+ cmd = f'vgimport {vg.name}'
+
+ debug(f'vgimport: {cmd}')
+ SysCommand(cmd)
+
+ def lvm_vol_reduce(self, vol_path: Path, amount: Size):
+ val = amount.format_size(Unit.B, include_unit=False)
+ cmd = f'lvreduce -L -{val}B {vol_path}'
+
+ debug(f'Reducing LVM volume size: {cmd}')
+ SysCommand(cmd)
+
+ def lvm_pv_create(self, pvs: Iterable[Path]):
+ cmd = 'pvcreate ' + ' '.join([str(pv) for pv in pvs])
+ debug(f'Creating LVM PVS: {cmd}')
+
+ worker = SysCommandWorker(cmd)
+ worker.poll()
+ worker.write(b'y\n', line_ending=False)
+
+ def lvm_vg_create(self, pvs: Iterable[Path], vg_name: str):
+ pvs_str = ' '.join([str(pv) for pv in pvs])
+ cmd = f'vgcreate --yes {vg_name} {pvs_str}'
+
+ debug(f'Creating LVM group: {cmd}')
+
+ worker = SysCommandWorker(cmd)
+ worker.poll()
+ worker.write(b'y\n', line_ending=False)
+
+ def lvm_vol_create(self, vg_name: str, volume: LvmVolume, offset: Optional[Size] = None):
+ if offset is not None:
+ length = volume.length - offset
+ else:
+ length = volume.length
+
+ length_str = length.format_size(Unit.B, include_unit=False)
+ cmd = f'lvcreate --yes -L {length_str}B {vg_name} -n {volume.name}'
+
+ debug(f'Creating volume: {cmd}')
+
+ worker = SysCommandWorker(cmd)
+ worker.poll()
+ worker.write(b'y\n', line_ending=False)
+
+ volume.vg_name = vg_name
+ volume.dev_path = Path(f'/dev/{vg_name}/{volume.name}')
+
+ def _setup_partition(
+ self,
+ part_mod: PartitionModification,
+ block_device: BDevice,
+ disk: Disk,
+ requires_delete: bool
+ ):
+ # when we require a delete and the partition to be (re)created
+ # already exists then we have to delete it first
+ if requires_delete and part_mod.status in [ModificationStatus.Modify, ModificationStatus.Delete]:
+ info(f'Delete existing partition: {part_mod.safe_dev_path}')
+ part_info = self.find_partition(part_mod.safe_dev_path)
+
+ if not part_info:
+ raise DiskError(f'No partition for dev path found: {part_mod.safe_dev_path}')
+
+ disk.deletePartition(part_info.partition)
+
+ if part_mod.status == ModificationStatus.Delete:
+ return
+
+ start_sector = part_mod.start.convert(
+ Unit.sectors,
+ block_device.device_info.sector_size
+ )
+
+ length_sector = part_mod.length.convert(
+ Unit.sectors,
+ block_device.device_info.sector_size
+ )
+
+ geometry = Geometry(
+ device=block_device.disk.device,
+ start=start_sector.value,
+ length=length_sector.value
+ )
+
+ filesystem = FileSystem(type=part_mod.safe_fs_type.value, geometry=geometry)
+
+ partition = Partition(
+ disk=disk,
+ type=part_mod.type.get_partition_code(),
+ fs=filesystem,
+ geometry=geometry
+ )
+
+ for flag in part_mod.flags:
+ partition.setFlag(flag.value)
+
+ debug(f'\tType: {part_mod.type.value}')
+ debug(f'\tFilesystem: {part_mod.safe_fs_type.value}')
+ debug(f'\tGeometry: {start_sector.value} start sector, {length_sector.value} length')
+
+ try:
+ disk.addPartition(partition=partition, constraint=disk.device.optimalAlignedConstraint)
+ except PartitionException as ex:
+ raise DiskError(f'Unable to add partition, most likely due to overlapping sectors: {ex}') from ex
+
+ # the partition has a path now that it has been added
+ part_mod.dev_path = Path(partition.path)
+
+ def fetch_part_info(self, path: Path) -> LsblkInfo:
+ lsblk_info = get_lsblk_info(path)
+
+ if not lsblk_info.partn:
+ debug(f'Unable to determine new partition number: {path}\n{lsblk_info}')
+ raise DiskError(f'Unable to determine new partition number: {path}')
+
+ if not lsblk_info.partuuid:
+ debug(f'Unable to determine new partition uuid: {path}\n{lsblk_info}')
+ raise DiskError(f'Unable to determine new partition uuid: {path}')
+
+ if not lsblk_info.uuid:
+ debug(f'Unable to determine new uuid: {path}\n{lsblk_info}')
+ raise DiskError(f'Unable to determine new uuid: {path}')
+
+ debug(f'partition information found: {lsblk_info.json()}')
+
+ return lsblk_info
+
+ def create_lvm_btrfs_subvolumes(
+ self,
+ path: Path,
+ btrfs_subvols: List[SubvolumeModification],
+ mount_options: List[str]
+ ):
+ info(f'Creating subvolumes: {path}')
+
+ self.mount(path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True)
+
+ for sub_vol in btrfs_subvols:
+ debug(f'Creating subvolume: {sub_vol.name}')
+
+ subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name
+
+ SysCommand(f"btrfs subvolume create {subvol_path}")
+
+ if BtrfsMountOption.nodatacow.value in mount_options:
+ try:
+ SysCommand(f'chattr +C {subvol_path}')
+ except SysCallError as err:
+ raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {err}')
+
+ if BtrfsMountOption.compress.value in mount_options:
+ try:
+ SysCommand(f'chattr +c {subvol_path}')
+ except SysCallError as err:
+ raise DiskError(f'Could not set compress attribute at {subvol_path}: {err}')
+
+ self.umount(path)
+
+ def create_btrfs_volumes(
+ self,
+ part_mod: PartitionModification,
+ enc_conf: Optional['DiskEncryption'] = None
+ ):
+ info(f'Creating subvolumes: {part_mod.safe_dev_path}')
+
+ luks_handler = None
+
+ # unlock the partition first if it's encrypted
+ if enc_conf is not None and part_mod in enc_conf.partitions:
+ if not part_mod.mapper_name:
+ raise ValueError('No device path specified for modification')
+
+ luks_handler = self.unlock_luks2_dev(
+ part_mod.safe_dev_path,
+ part_mod.mapper_name,
+ enc_conf.encryption_password
+ )
+
+ if not luks_handler.mapper_dev:
+ raise DiskError('Failed to unlock luks device')
+
+ self.mount(
+ luks_handler.mapper_dev,
+ self._TMP_BTRFS_MOUNT,
+ create_target_mountpoint=True,
+ options=part_mod.mount_options
+ )
+ else:
+ self.mount(
+ part_mod.safe_dev_path,
+ self._TMP_BTRFS_MOUNT,
+ create_target_mountpoint=True,
+ options=part_mod.mount_options
+ )
+
+ for sub_vol in part_mod.btrfs_subvols:
+ debug(f'Creating subvolume: {sub_vol.name}')
+
+ if luks_handler is not None:
+ subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name
+ else:
+ subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name
+
+ SysCommand(f"btrfs subvolume create {subvol_path}")
+
+ if luks_handler is not None and luks_handler.mapper_dev is not None:
+ self.umount(luks_handler.mapper_dev)
+ luks_handler.lock()
+ else:
+ self.umount(part_mod.safe_dev_path)
+
+ def unlock_luks2_dev(self, dev_path: Path, mapper_name: str, enc_password: str) -> Luks2:
+ luks_handler = Luks2(dev_path, mapper_name=mapper_name, password=enc_password)
+
+ if not luks_handler.is_unlocked():
+ luks_handler.unlock()
+
+ if not luks_handler.is_unlocked():
+ raise DiskError(f'Failed to unlock luks2 device: {dev_path}')
+
+ return luks_handler
+
+ def umount_all_existing(self, device_path: Path):
+ debug(f'Unmounting all existing partitions: {device_path}')
+
+ existing_partitions = self._devices[device_path].partition_infos
+
+ for partition in existing_partitions:
+ debug(f'Unmounting: {partition.path}')
+
+ # un-mount for existing encrypted partitions
+ if partition.fs_type == FilesystemType.Crypto_luks:
+ Luks2(partition.path).lock()
+ else:
+ self.umount(partition.path, recursive=True)
+
+ def partition(
+ self,
+ modification: DeviceModification,
+ partition_table: Optional[PartitionTable] = None
+ ):
+ """
+ Create a partition table on the block device and create all partitions.
+ """
+ if modification.wipe:
+ if partition_table is None:
+ raise ValueError('Modification is marked as wipe but no partitioning table was provided')
+
+ if partition_table.MBR and len(modification.partitions) > 3:
+ raise DiskError('Too many partitions on disk, MBR disks can only have 3 primary partitions')
+
+ # make sure all devices are unmounted
+ self.umount_all_existing(modification.device_path)
+
+ # WARNING: the entire device will be wiped and all data lost
+ if modification.wipe:
+ self.wipe_dev(modification.device)
+ part_table = partition_table.value if partition_table else None
+ disk = freshDisk(modification.device.disk.device, part_table)
+ else:
+ info(f'Use existing device: {modification.device_path}')
+ disk = modification.device.disk
+
+ info(f'Creating partitions: {modification.device_path}')
+
+ # don't touch existing partitions
+ filtered_part = [p for p in modification.partitions if not p.exists()]
+
+ for part_mod in filtered_part:
+ # if the entire disk got nuked then we don't have to delete
+ # any existing partitions anymore because they're all gone already
+ requires_delete = modification.wipe is False
+ self._setup_partition(part_mod, modification.device, disk, requires_delete=requires_delete)
+
+ disk.commit()
+
+ def mount(
+ self,
+ dev_path: Path,
+ target_mountpoint: Path,
+ mount_fs: Optional[str] = None,
+ create_target_mountpoint: bool = True,
+ options: List[str] = []
+ ):
+ if create_target_mountpoint and not target_mountpoint.exists():
+ target_mountpoint.mkdir(parents=True, exist_ok=True)
+
+ if not target_mountpoint.exists():
+ raise ValueError('Target mountpoint does not exist')
+
+ lsblk_info = get_lsblk_info(dev_path)
+ if target_mountpoint in lsblk_info.mountpoints:
+ info(f'Device already mounted at {target_mountpoint}')
+ return
+
+ cmd = ['mount']
+
+ if len(options):
+ cmd.extend(('-o', ','.join(options)))
+ if mount_fs:
+ cmd.extend(('-t', mount_fs))
+
+ cmd.extend((str(dev_path), str(target_mountpoint)))
+
+ command = ' '.join(cmd)
+
+ debug(f'Mounting {dev_path}: {command}')
+
+ try:
+ SysCommand(command)
+ except SysCallError as err:
+ raise DiskError(f'Could not mount {dev_path}: {command}\n{err.message}')
+
+ def umount(self, mountpoint: Path, recursive: bool = False):
+ try:
+ lsblk_info = get_lsblk_info(mountpoint)
+ except SysCallError as ex:
+ # this could happen if before partitioning the device contained 3 partitions
+ # and after partitioning only 2 partitions were created, then the modifications object
+ # will have a reference to /dev/sX3 which is being tried to umount here now
+ if 'not a block device' in ex.message:
+ return
+ raise ex
+
+ if len(lsblk_info.mountpoints) > 0:
+ debug(f'Partition {mountpoint} is currently mounted at: {[str(m) for m in lsblk_info.mountpoints]}')
+
+ for mountpoint in lsblk_info.mountpoints:
+ debug(f'Unmounting mountpoint: {mountpoint}')
+
+ command = 'umount'
+
+ if recursive:
+ command += ' -R'
+
+ SysCommand(f'{command} {mountpoint}')
+
+ def detect_pre_mounted_mods(self, base_mountpoint: Path) -> List[DeviceModification]:
+ part_mods: Dict[Path, List[PartitionModification]] = {}
+
+ for device in self.devices:
+ for part_info in device.partition_infos:
+ for mountpoint in part_info.mountpoints:
+ if is_subpath(mountpoint, base_mountpoint):
+ path = Path(part_info.disk.device.path)
+ part_mods.setdefault(path, [])
+ part_mod = PartitionModification.from_existing_partition(part_info)
+ if part_mod.mountpoint:
+ part_mod.mountpoint = mountpoint.root / mountpoint.relative_to(base_mountpoint)
+ else:
+ for subvol in part_mod.btrfs_subvols:
+ if sm := subvol.mountpoint:
+ subvol.mountpoint = sm.root / sm.relative_to(base_mountpoint)
+ part_mods[path].append(part_mod)
+ break
+
+ device_mods: List[DeviceModification] = []
+ for device_path, mods in part_mods.items():
+ device_mod = DeviceModification(self._devices[device_path], False, mods)
+ device_mods.append(device_mod)
+
+ return device_mods
+
+ def partprobe(self, path: Optional[Path] = None):
+ if path is not None:
+ command = f'partprobe {path}'
+ else:
+ command = 'partprobe'
+
+ try:
+ debug(f'Calling partprobe: {command}')
+ SysCommand(command)
+ except SysCallError as err:
+ if 'have been written, but we have been unable to inform the kernel of the change' in str(err):
+ log(f"Partprobe was not able to inform the kernel of the new disk state (ignoring error): {err}", fg="gray", level=logging.INFO)
+ else:
+ error(f'"{command}" failed to run (continuing anyway): {err}')
+
+ def _wipe(self, dev_path: Path):
+ """
+ Wipe a device (partition or otherwise) of meta-data, be it file system, LVM, etc.
+ @param dev_path: Device path of the partition to be wiped.
+ @type dev_path: str
+ """
+ with open(dev_path, 'wb') as p:
+ p.write(bytearray(1024))
+
+ def wipe_dev(self, block_device: BDevice):
+ """
+ Wipe the block device of meta-data, be it file system, LVM, etc.
+ This is not intended to be secure, but rather to ensure that
+ auto-discovery tools don't recognize anything here.
+ """
+ info(f'Wiping partitions and metadata: {block_device.device_info.path}')
+ for partition in block_device.partition_infos:
+ self._wipe(partition.path)
+
+ self._wipe(block_device.device_info.path)
+
+
+device_handler = DeviceHandler()
+
+
+def disk_layouts() -> str:
+ try:
+ lsblk_info = get_all_lsblk_info()
+ return json.dumps(lsblk_info, indent=4, sort_keys=True, cls=JSON)
+ except SysCallError as err:
+ warn(f"Could not return disk layouts: {err}")
+ return ''
+ except json.decoder.JSONDecodeError as err:
+ warn(f"Could not return disk layouts: {err}")
+ return ''
diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py
new file mode 100644
index 00000000..f98d05fb
--- /dev/null
+++ b/archinstall/lib/disk/device_model.py
@@ -0,0 +1,1499 @@
+from __future__ import annotations
+
+import dataclasses
+import json
+import math
+import uuid
+from dataclasses import dataclass, field
+from enum import Enum
+from enum import auto
+from pathlib import Path
+from typing import Optional, List, Dict, TYPE_CHECKING, Any
+from typing import Union
+
+import parted # type: ignore
+import _ped # type: ignore
+from parted import Disk, Geometry, Partition
+
+from ..exceptions import DiskError, SysCallError
+from ..general import SysCommand
+from ..output import debug, error
+from ..storage import storage
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class DiskLayoutType(Enum):
+ Default = 'default_layout'
+ Manual = 'manual_partitioning'
+ Pre_mount = 'pre_mounted_config'
+
+ def display_msg(self) -> str:
+ match self:
+ case DiskLayoutType.Default: return str(_('Use a best-effort default partition layout'))
+ case DiskLayoutType.Manual: return str(_('Manual Partitioning'))
+ case DiskLayoutType.Pre_mount: return str(_('Pre-mounted configuration'))
+
+
+@dataclass
+class DiskLayoutConfiguration:
+ config_type: DiskLayoutType
+ device_modifications: List[DeviceModification] = field(default_factory=list)
+ lvm_config: Optional[LvmConfiguration] = None
+
+ # used for pre-mounted config
+ mountpoint: Optional[Path] = None
+
+ def json(self) -> Dict[str, Any]:
+ if self.config_type == DiskLayoutType.Pre_mount:
+ return {
+ 'config_type': self.config_type.value,
+ 'mountpoint': str(self.mountpoint)
+ }
+ else:
+ config: Dict[str, Any] = {
+ 'config_type': self.config_type.value,
+ 'device_modifications': [mod.json() for mod in self.device_modifications],
+ }
+
+ if self.lvm_config:
+ config['lvm_config'] = self.lvm_config.json()
+
+ return config
+
+ @classmethod
+ def parse_arg(cls, disk_config: Dict[str, Any]) -> Optional[DiskLayoutConfiguration]:
+ from .device_handler import device_handler
+
+ device_modifications: List[DeviceModification] = []
+ config_type = disk_config.get('config_type', None)
+
+ if not config_type:
+ raise ValueError('Missing disk layout configuration: config_type')
+
+ config = DiskLayoutConfiguration(
+ config_type=DiskLayoutType(config_type),
+ device_modifications=device_modifications
+ )
+
+ if config_type == DiskLayoutType.Pre_mount.value:
+ if not (mountpoint := disk_config.get('mountpoint')):
+ raise ValueError('Must set a mountpoint when layout type is pre-mount')
+
+ path = Path(str(mountpoint))
+
+ mods = device_handler.detect_pre_mounted_mods(path)
+ device_modifications.extend(mods)
+
+ storage['MOUNT_POINT'] = path
+
+ config.mountpoint = path
+
+ return config
+
+ for entry in disk_config.get('device_modifications', []):
+ device_path = Path(entry.get('device', None)) if entry.get('device', None) else None
+
+ if not device_path:
+ continue
+
+ device = device_handler.get_device(device_path)
+
+ if not device:
+ continue
+
+ device_modification = DeviceModification(
+ wipe=entry.get('wipe', False),
+ device=device
+ )
+
+ device_partitions: List[PartitionModification] = []
+
+ for partition in entry.get('partitions', []):
+ device_partition = PartitionModification(
+ status=ModificationStatus(partition['status']),
+ fs_type=FilesystemType(partition['fs_type']) if partition.get('fs_type') else None,
+ start=Size.parse_args(partition['start']),
+ length=Size.parse_args(partition['size']),
+ mount_options=partition['mount_options'],
+ mountpoint=Path(partition['mountpoint']) if partition['mountpoint'] else None,
+ dev_path=Path(partition['dev_path']) if partition['dev_path'] else None,
+ type=PartitionType(partition['type']),
+ flags=[PartitionFlag[f] for f in partition.get('flags', [])],
+ btrfs_subvols=SubvolumeModification.parse_args(partition.get('btrfs', [])),
+ )
+ # special 'invisible attr to internally identify the part mod
+ setattr(device_partition, '_obj_id', partition['obj_id'])
+ device_partitions.append(device_partition)
+
+ device_modification.partitions = device_partitions
+ device_modifications.append(device_modification)
+
+ # Parse LVM configuration from settings
+ if (lvm_arg := disk_config.get('lvm_config', None)) is not None:
+ config.lvm_config = LvmConfiguration.parse_arg(lvm_arg, config)
+
+ return config
+
+
+class PartitionTable(Enum):
+ GPT = 'gpt'
+ MBR = 'msdos'
+
+
+class Unit(Enum):
+ B = 1 # byte
+ kB = 1000 ** 1 # kilobyte
+ MB = 1000 ** 2 # megabyte
+ GB = 1000 ** 3 # gigabyte
+ TB = 1000 ** 4 # terabyte
+ PB = 1000 ** 5 # petabyte
+ EB = 1000 ** 6 # exabyte
+ ZB = 1000 ** 7 # zettabyte
+ YB = 1000 ** 8 # yottabyte
+
+ KiB = 1024 ** 1 # kibibyte
+ MiB = 1024 ** 2 # mebibyte
+ GiB = 1024 ** 3 # gibibyte
+ TiB = 1024 ** 4 # tebibyte
+ PiB = 1024 ** 5 # pebibyte
+ EiB = 1024 ** 6 # exbibyte
+ ZiB = 1024 ** 7 # zebibyte
+ YiB = 1024 ** 8 # yobibyte
+
+ sectors = 'sectors' # size in sector
+
+ @staticmethod
+ def get_all_units() -> List[str]:
+ return [u.name for u in Unit]
+
+ @staticmethod
+ def get_si_units() -> List[Unit]:
+ return [u for u in Unit if 'i' not in u.name and u.name != 'sectors']
+
+
+@dataclass
+class SectorSize:
+ value: int
+ unit: Unit
+
+ def __post_init__(self):
+ match self.unit:
+ case Unit.sectors:
+ raise ValueError('Unit type sector not allowed for SectorSize')
+
+ @staticmethod
+ def default() -> SectorSize:
+ return SectorSize(512, Unit.B)
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'value': self.value,
+ 'unit': self.unit.name,
+ }
+
+ @classmethod
+ def parse_args(cls, arg: Dict[str, Any]) -> SectorSize:
+ return SectorSize(
+ arg['value'],
+ Unit[arg['unit']]
+ )
+
+ def normalize(self) -> int:
+ """
+ will normalize the value of the unit to Byte
+ """
+ return int(self.value * self.unit.value) # type: ignore
+
+
+@dataclass
+class Size:
+ value: int
+ unit: Unit
+ sector_size: SectorSize
+
+ def __post_init__(self):
+ if not isinstance(self.sector_size, SectorSize):
+ raise ValueError('sector size must be of type SectorSize')
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'value': self.value,
+ 'unit': self.unit.name,
+ 'sector_size': self.sector_size.json() if self.sector_size else None
+ }
+
+ @classmethod
+ def parse_args(cls, size_arg: Dict[str, Any]) -> Size:
+ sector_size = size_arg['sector_size']
+
+ return Size(
+ size_arg['value'],
+ Unit[size_arg['unit']],
+ SectorSize.parse_args(sector_size),
+ )
+
+ def convert(
+ self,
+ target_unit: Unit,
+ sector_size: Optional[SectorSize] = None
+ ) -> Size:
+ if target_unit == Unit.sectors and sector_size is None:
+ raise ValueError('If target has unit sector, a sector size must be provided')
+
+ if self.unit == target_unit:
+ return self
+ elif self.unit == Unit.sectors:
+ norm = self._normalize()
+ return Size(norm, Unit.B, self.sector_size).convert(target_unit, sector_size)
+ else:
+ if target_unit == Unit.sectors and sector_size is not None:
+ norm = self._normalize()
+ sectors = math.ceil(norm / sector_size.value)
+ return Size(sectors, Unit.sectors, sector_size)
+ else:
+ value = int(self._normalize() / target_unit.value) # type: ignore
+ return Size(value, target_unit, self.sector_size)
+
+ def as_text(self) -> str:
+ return self.format_size(
+ self.unit,
+ self.sector_size
+ )
+
+ def format_size(
+ self,
+ target_unit: Unit,
+ sector_size: Optional[SectorSize] = None,
+ include_unit: bool = True
+ ) -> str:
+ target_size = self.convert(target_unit, sector_size)
+
+ if include_unit:
+ return f'{target_size.value} {target_unit.name}'
+ return f'{target_size.value}'
+
+ def format_highest(self, include_unit: bool = True) -> str:
+ si_units = Unit.get_si_units()
+ all_si_values = [self.convert(si) for si in si_units]
+ filtered = filter(lambda x: x.value >= 1, all_si_values)
+
+ # we have to get the max by the unit value as we're interested
+ # in getting the value in the highest possible unit without floats
+ si_value = max(filtered, key=lambda x: x.unit.value)
+
+ if include_unit:
+ return f'{si_value.value} {si_value.unit.name}'
+ return f'{si_value.value}'
+
+ def _normalize(self) -> int:
+ """
+ will normalize the value of the unit to Byte
+ """
+ if self.unit == Unit.sectors and self.sector_size is not None:
+ return self.value * self.sector_size.normalize()
+ return int(self.value * self.unit.value) # type: ignore
+
+ def __sub__(self, other: Size) -> Size:
+ src_norm = self._normalize()
+ dest_norm = other._normalize()
+ return Size(abs(src_norm - dest_norm), Unit.B, self.sector_size)
+
+ def __add__(self, other: Size) -> Size:
+ src_norm = self._normalize()
+ dest_norm = other._normalize()
+ return Size(abs(src_norm + dest_norm), Unit.B, self.sector_size)
+
+ def __lt__(self, other):
+ return self._normalize() < other._normalize()
+
+ def __le__(self, other):
+ return self._normalize() <= other._normalize()
+
+ def __eq__(self, other):
+ return self._normalize() == other._normalize()
+
+ def __ne__(self, other):
+ return self._normalize() != other._normalize()
+
+ def __gt__(self, other):
+ return self._normalize() > other._normalize()
+
+ def __ge__(self, other):
+ return self._normalize() >= other._normalize()
+
+
+class BtrfsMountOption(Enum):
+ compress = 'compress=zstd'
+ nodatacow = 'nodatacow'
+
+
+@dataclass
+class _BtrfsSubvolumeInfo:
+ name: Path
+ mountpoint: Optional[Path]
+
+
+@dataclass
+class _PartitionInfo:
+ partition: Partition
+ name: str
+ type: PartitionType
+ fs_type: Optional[FilesystemType]
+ path: Path
+ start: Size
+ length: Size
+ flags: List[PartitionFlag]
+ partn: Optional[int]
+ partuuid: Optional[str]
+ uuid: Optional[str]
+ disk: Disk
+ mountpoints: List[Path]
+ btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list)
+
+ @property
+ def sector_size(self) -> SectorSize:
+ sector_size = self.partition.geometry.device.sectorSize
+ return SectorSize(sector_size, Unit.B)
+
+ def table_data(self) -> Dict[str, Any]:
+ end = self.start + self.length
+
+ part_info = {
+ 'Name': self.name,
+ 'Type': self.type.value,
+ 'Filesystem': self.fs_type.value if self.fs_type else str(_('Unknown')),
+ 'Path': str(self.path),
+ 'Start': self.start.format_size(Unit.sectors, self.sector_size, include_unit=False),
+ 'End': end.format_size(Unit.sectors, self.sector_size, include_unit=False),
+ 'Size': self.length.format_highest(),
+ 'Flags': ', '.join([f.name for f in self.flags])
+ }
+
+ if self.btrfs_subvol_infos:
+ part_info['Btrfs vol.'] = f'{len(self.btrfs_subvol_infos)} subvolumes'
+
+ return part_info
+
+ @classmethod
+ def from_partition(
+ cls,
+ partition: Partition,
+ fs_type: Optional[FilesystemType],
+ partn: Optional[int],
+ partuuid: Optional[str],
+ uuid: Optional[str],
+ mountpoints: List[Path],
+ btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = []
+ ) -> _PartitionInfo:
+ partition_type = PartitionType.get_type_from_code(partition.type)
+ flags = [f for f in PartitionFlag if partition.getFlag(f.value)]
+
+ start = Size(
+ partition.geometry.start,
+ Unit.sectors,
+ SectorSize(partition.disk.device.sectorSize, Unit.B)
+ )
+
+ length = Size(
+ int(partition.getLength(unit='B')),
+ Unit.B,
+ SectorSize(partition.disk.device.sectorSize, Unit.B)
+ )
+
+ return _PartitionInfo(
+ partition=partition,
+ name=partition.get_name(),
+ type=partition_type,
+ fs_type=fs_type,
+ path=partition.path,
+ start=start,
+ length=length,
+ flags=flags,
+ partn=partn,
+ partuuid=partuuid,
+ uuid=uuid,
+ disk=partition.disk,
+ mountpoints=mountpoints,
+ btrfs_subvol_infos=btrfs_subvol_infos
+ )
+
+
+@dataclass
+class _DeviceInfo:
+ model: str
+ path: Path
+ type: str
+ total_size: Size
+ free_space_regions: List[DeviceGeometry]
+ sector_size: SectorSize
+ read_only: bool
+ dirty: bool
+
+ def table_data(self) -> Dict[str, Any]:
+ total_free_space = sum([region.get_length(unit=Unit.MiB) for region in self.free_space_regions])
+ return {
+ 'Model': self.model,
+ 'Path': str(self.path),
+ 'Type': self.type,
+ 'Size': self.total_size.format_highest(),
+ 'Free space': int(total_free_space),
+ 'Sector size': self.sector_size.value,
+ 'Read only': self.read_only
+ }
+
+ @classmethod
+ def from_disk(cls, disk: Disk) -> _DeviceInfo:
+ device = disk.device
+ if device.type == 18:
+ device_type = 'loop'
+ elif device.type in parted.devices:
+ device_type = parted.devices[device.type]
+ else:
+ debug(f'Device code unknown: {device.type}')
+ device_type = parted.devices[parted.DEVICE_UNKNOWN]
+
+ sector_size = SectorSize(device.sectorSize, Unit.B)
+ free_space = [DeviceGeometry(g, sector_size) for g in disk.getFreeSpaceRegions()]
+
+ sector_size = SectorSize(device.sectorSize, Unit.B)
+
+ return _DeviceInfo(
+ model=device.model.strip(),
+ path=Path(device.path),
+ type=device_type,
+ sector_size=sector_size,
+ total_size=Size(int(device.getLength(unit='B')), Unit.B, sector_size),
+ free_space_regions=free_space,
+ read_only=device.readOnly,
+ dirty=device.dirty
+ )
+
+
+@dataclass
+class SubvolumeModification:
+ name: Path
+ mountpoint: Optional[Path] = None
+
+ @classmethod
+ def from_existing_subvol_info(cls, info: _BtrfsSubvolumeInfo) -> SubvolumeModification:
+ return SubvolumeModification(info.name, mountpoint=info.mountpoint)
+
+ @classmethod
+ def parse_args(cls, subvol_args: List[Dict[str, Any]]) -> List[SubvolumeModification]:
+ mods = []
+ for entry in subvol_args:
+ if not entry.get('name', None) or not entry.get('mountpoint', None):
+ debug(f'Subvolume arg is missing name: {entry}')
+ continue
+
+ mountpoint = Path(entry['mountpoint']) if entry['mountpoint'] else None
+
+ mods.append(SubvolumeModification(entry['name'], mountpoint))
+
+ return mods
+
+ @property
+ def relative_mountpoint(self) -> Path:
+ """
+ Will return the relative path based on the anchor
+ e.g. Path('/mnt/test') -> Path('mnt/test')
+ """
+ if self.mountpoint is not None:
+ return self.mountpoint.relative_to(self.mountpoint.anchor)
+
+ raise ValueError('Mountpoint is not specified')
+
+ def is_root(self) -> bool:
+ if self.mountpoint:
+ return self.mountpoint == Path('/')
+ return False
+
+ def json(self) -> Dict[str, Any]:
+ return {'name': str(self.name), 'mountpoint': str(self.mountpoint)}
+
+ def table_data(self) -> Dict[str, Any]:
+ return self.json()
+
+
+class DeviceGeometry:
+ def __init__(self, geometry: Geometry, sector_size: SectorSize):
+ self._geometry = geometry
+ self._sector_size = sector_size
+
+ @property
+ def start(self) -> int:
+ return self._geometry.start
+
+ @property
+ def end(self) -> int:
+ return self._geometry.end
+
+ def get_length(self, unit: Unit = Unit.sectors) -> int:
+ return self._geometry.getLength(unit.name)
+
+ def table_data(self) -> Dict[str, Any]:
+ start = Size(self._geometry.start, Unit.sectors, self._sector_size)
+ end = Size(self._geometry.end, Unit.sectors, self._sector_size)
+ length = Size(self._geometry.getLength(), Unit.sectors, self._sector_size)
+
+ start_str = f'{self._geometry.start} / {start.format_size(Unit.B, include_unit=False)}'
+ end_str = f'{self._geometry.end} / {end.format_size(Unit.B, include_unit=False)}'
+ length_str = f'{self._geometry.getLength()} / {length.format_size(Unit.B, include_unit=False)}'
+
+ return {
+ 'Sector size': self._sector_size.value,
+ 'Start (sector/B)': start_str,
+ 'End (sector/B)': end_str,
+ 'Size (sectors/B)': length_str
+ }
+
+
+@dataclass
+class BDevice:
+ disk: Disk
+ device_info: _DeviceInfo
+ partition_infos: List[_PartitionInfo]
+
+ def __hash__(self):
+ return hash(self.disk.device.path)
+
+
+class PartitionType(Enum):
+ Boot = 'boot'
+ Primary = 'primary'
+ _Unknown = 'unknown'
+
+ @classmethod
+ def get_type_from_code(cls, code: int) -> PartitionType:
+ if code == parted.PARTITION_NORMAL:
+ return PartitionType.Primary
+ else:
+ debug(f'Partition code not supported: {code}')
+ return PartitionType._Unknown
+
+ def get_partition_code(self) -> Optional[int]:
+ if self == PartitionType.Primary:
+ return parted.PARTITION_NORMAL
+ elif self == PartitionType.Boot:
+ return parted.PARTITION_BOOT
+ return None
+
+
+class PartitionFlag(Enum):
+ """
+ Flags are taken from _ped because pyparted uses this to look
+ up their flag definitions: https://github.com/dcantrell/pyparted/blob/c4e0186dad45c8efbe67c52b02c8c4319df8aa9b/src/parted/__init__.py#L200-L202
+ Which is the way libparted checks for its flags: https://git.savannah.gnu.org/gitweb/?p=parted.git;a=blob;f=libparted/labels/gpt.c;hb=4a0e468ed63fff85a1f9b923189f20945b32f4f1#l183
+ """
+ Boot = _ped.PARTITION_BOOT
+ XBOOTLDR = _ped.PARTITION_BLS_BOOT # Note: parted calls this bls_boot
+ ESP = _ped.PARTITION_ESP
+
+
+# class PartitionGUIDs(Enum):
+# """
+# A list of Partition type GUIDs (lsblk -o+PARTTYPE) can be found here: https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
+# """
+# XBOOTLDR = 'bc13c2ff-59e6-4262-a352-b275fd6f7172'
+
+
+class FilesystemType(Enum):
+ Btrfs = 'btrfs'
+ Ext2 = 'ext2'
+ Ext3 = 'ext3'
+ Ext4 = 'ext4'
+ F2fs = 'f2fs'
+ Fat16 = 'fat16'
+ Fat32 = 'fat32'
+ Ntfs = 'ntfs'
+ Reiserfs = 'reiserfs'
+ Xfs = 'xfs'
+
+ # this is not a FS known to parted, so be careful
+ # with the usage from this enum
+ Crypto_luks = 'crypto_LUKS'
+
+ def is_crypto(self) -> bool:
+ return self == FilesystemType.Crypto_luks
+
+ @property
+ def fs_type_mount(self) -> str:
+ match self:
+ case FilesystemType.Ntfs: return 'ntfs3'
+ case FilesystemType.Fat32: return 'vfat'
+ case _: return self.value # type: ignore
+
+ @property
+ def installation_pkg(self) -> Optional[str]:
+ match self:
+ case FilesystemType.Btrfs: return 'btrfs-progs'
+ case FilesystemType.Xfs: return 'xfsprogs'
+ case FilesystemType.F2fs: return 'f2fs-tools'
+ case _: return None
+
+ @property
+ def installation_module(self) -> Optional[str]:
+ match self:
+ case FilesystemType.Btrfs: return 'btrfs'
+ case _: return None
+
+ @property
+ def installation_binary(self) -> Optional[str]:
+ match self:
+ case FilesystemType.Btrfs: return '/usr/bin/btrfs'
+ case _: return None
+
+ @property
+ def installation_hooks(self) -> Optional[str]:
+ match self:
+ case FilesystemType.Btrfs: return 'btrfs'
+ case _: return None
+
+
+class ModificationStatus(Enum):
+ Exist = 'existing'
+ Modify = 'modify'
+ Delete = 'delete'
+ Create = 'create'
+
+
+@dataclass
+class PartitionModification:
+ status: ModificationStatus
+ type: PartitionType
+ start: Size
+ length: Size
+ fs_type: Optional[FilesystemType] = None
+ mountpoint: Optional[Path] = None
+ mount_options: List[str] = field(default_factory=list)
+ flags: List[PartitionFlag] = field(default_factory=list)
+ btrfs_subvols: List[SubvolumeModification] = field(default_factory=list)
+
+ # only set if the device was created or exists
+ dev_path: Optional[Path] = None
+ partn: Optional[int] = None
+ partuuid: Optional[str] = None
+ uuid: Optional[str] = None
+
+ _efi_indicator_flags = (PartitionFlag.Boot, PartitionFlag.ESP)
+ _boot_indicator_flags = (PartitionFlag.Boot, PartitionFlag.XBOOTLDR)
+
+ def __post_init__(self):
+ # needed to use the object as a dictionary key due to hash func
+ if not hasattr(self, '_obj_id'):
+ self._obj_id = uuid.uuid4()
+
+ if self.is_exists_or_modify() and not self.dev_path:
+ raise ValueError('If partition marked as existing a path must be set')
+
+ if self.fs_type is None and self.status == ModificationStatus.Modify:
+ raise ValueError('FS type must not be empty on modifications with status type modify')
+
+ def __hash__(self):
+ return hash(self._obj_id)
+
+ @property
+ def end(self) -> Size:
+ return self.start + self.length
+
+ @property
+ def obj_id(self) -> str:
+ if hasattr(self, '_obj_id'):
+ return str(self._obj_id)
+ return ''
+
+ @property
+ def safe_dev_path(self) -> Path:
+ if self.dev_path is None:
+ raise ValueError('Device path was not set')
+ return self.dev_path
+
+ @property
+ def safe_fs_type(self) -> FilesystemType:
+ if self.fs_type is None:
+ raise ValueError('File system type is not set')
+ return self.fs_type
+
+ @classmethod
+ def from_existing_partition(cls, partition_info: _PartitionInfo) -> PartitionModification:
+ if partition_info.btrfs_subvol_infos:
+ mountpoint = None
+ subvol_mods = []
+ for i in partition_info.btrfs_subvol_infos:
+ subvol_mods.append(
+ SubvolumeModification.from_existing_subvol_info(i)
+ )
+ else:
+ mountpoint = partition_info.mountpoints[0] if partition_info.mountpoints else None
+ subvol_mods = []
+
+ return PartitionModification(
+ status=ModificationStatus.Exist,
+ type=partition_info.type,
+ start=partition_info.start,
+ length=partition_info.length,
+ fs_type=partition_info.fs_type,
+ dev_path=partition_info.path,
+ partn=partition_info.partn,
+ partuuid=partition_info.partuuid,
+ uuid=partition_info.uuid,
+ flags=partition_info.flags,
+ mountpoint=mountpoint,
+ btrfs_subvols=subvol_mods
+ )
+
+ @property
+ def relative_mountpoint(self) -> Path:
+ """
+ Will return the relative path based on the anchor
+ e.g. Path('/mnt/test') -> Path('mnt/test')
+ """
+ if self.mountpoint:
+ return self.mountpoint.relative_to(self.mountpoint.anchor)
+
+ raise ValueError('Mountpoint is not specified')
+
+ def is_efi(self) -> bool:
+ return (
+ any(set(self.flags) & set(self._efi_indicator_flags))
+ and self.fs_type == FilesystemType.Fat32
+ and PartitionFlag.XBOOTLDR not in self.flags
+ )
+
+ def is_boot(self) -> bool:
+ """
+ Returns True if any of the boot indicator flags are found in self.flags
+ """
+ return any(set(self.flags) & set(self._boot_indicator_flags))
+
+ def is_root(self) -> bool:
+ if self.mountpoint is not None:
+ return Path('/') == self.mountpoint
+ else:
+ for subvol in self.btrfs_subvols:
+ if subvol.is_root():
+ return True
+
+ return False
+
+ def is_modify(self) -> bool:
+ return self.status == ModificationStatus.Modify
+
+ def exists(self) -> bool:
+ return self.status == ModificationStatus.Exist
+
+ def is_exists_or_modify(self) -> bool:
+ return self.status in [ModificationStatus.Exist, ModificationStatus.Modify]
+
+ def is_create_or_modify(self) -> bool:
+ return self.status in [ModificationStatus.Create, ModificationStatus.Modify]
+
+ @property
+ def mapper_name(self) -> Optional[str]:
+ if self.dev_path:
+ return f'{storage.get("ENC_IDENTIFIER", "ai")}{self.dev_path.name}'
+ return None
+
+ def set_flag(self, flag: PartitionFlag):
+ if flag not in self.flags:
+ self.flags.append(flag)
+
+ def invert_flag(self, flag: PartitionFlag):
+ if flag in self.flags:
+ self.flags = [f for f in self.flags if f != flag]
+ else:
+ self.set_flag(flag)
+
+ def json(self) -> Dict[str, Any]:
+ """
+ Called for configuration settings
+ """
+ return {
+ 'obj_id': self.obj_id,
+ 'status': self.status.value,
+ 'type': self.type.value,
+ 'start': self.start.json(),
+ 'size': self.length.json(),
+ 'fs_type': self.fs_type.value if self.fs_type else None,
+ 'mountpoint': str(self.mountpoint) if self.mountpoint else None,
+ 'mount_options': self.mount_options,
+ 'flags': [f.name for f in self.flags],
+ 'dev_path': str(self.dev_path) if self.dev_path else None,
+ 'btrfs': [vol.json() for vol in self.btrfs_subvols]
+ }
+
+ def table_data(self) -> Dict[str, Any]:
+ """
+ Called for displaying data in table format
+ """
+ part_mod = {
+ 'Status': self.status.value,
+ 'Device': str(self.dev_path) if self.dev_path else '',
+ 'Type': self.type.value,
+ 'Start': self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False),
+ 'End': self.end.format_size(Unit.sectors, self.start.sector_size, include_unit=False),
+ 'Size': self.length.format_highest(),
+ 'FS type': self.fs_type.value if self.fs_type else 'Unknown',
+ 'Mountpoint': self.mountpoint if self.mountpoint else '',
+ 'Mount options': ', '.join(self.mount_options),
+ 'Flags': ', '.join([f.name for f in self.flags]),
+ }
+
+ if self.btrfs_subvols:
+ part_mod['Btrfs vol.'] = f'{len(self.btrfs_subvols)} subvolumes'
+
+ return part_mod
+
+
+class LvmLayoutType(Enum):
+ Default = 'default'
+
+ # Manual = 'manual_lvm'
+
+ def display_msg(self) -> str:
+ match self:
+ case LvmLayoutType.Default:
+ return str(_('Default layout'))
+ # case LvmLayoutType.Manual:
+ # return str(_('Manual configuration'))
+
+ raise ValueError(f'Unknown type: {self}')
+
+
+@dataclass
+class LvmVolumeGroup:
+ name: str
+ pvs: List[PartitionModification]
+ volumes: List[LvmVolume] = field(default_factory=list)
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'name': self.name,
+ 'lvm_pvs': [p.obj_id for p in self.pvs],
+ 'volumes': [vol.json() for vol in self.volumes]
+ }
+
+ @staticmethod
+ def parse_arg(arg: Dict[str, Any], disk_config: DiskLayoutConfiguration) -> LvmVolumeGroup:
+ lvm_pvs = []
+ for mod in disk_config.device_modifications:
+ for part in mod.partitions:
+ if part.obj_id in arg.get('lvm_pvs', []):
+ lvm_pvs.append(part)
+
+ return LvmVolumeGroup(
+ arg['name'],
+ lvm_pvs,
+ [LvmVolume.parse_arg(vol) for vol in arg['volumes']]
+ )
+
+ def contains_lv(self, lv: LvmVolume) -> bool:
+ return lv in self.volumes
+
+
+class LvmVolumeStatus(Enum):
+ Exist = 'existing'
+ Modify = 'modify'
+ Delete = 'delete'
+ Create = 'create'
+
+
+@dataclass
+class LvmVolume:
+ status: LvmVolumeStatus
+ name: str
+ fs_type: FilesystemType
+ length: Size
+ mountpoint: Optional[Path]
+ mount_options: List[str] = field(default_factory=list)
+ btrfs_subvols: List[SubvolumeModification] = field(default_factory=list)
+
+ # volume group name
+ vg_name: Optional[str] = None
+ # mapper device path /dev/<vg>/<vol>
+ dev_path: Optional[Path] = None
+
+ def __post_init__(self):
+ # needed to use the object as a dictionary key due to hash func
+ if not hasattr(self, '_obj_id'):
+ self._obj_id = uuid.uuid4()
+
+ def __hash__(self):
+ return hash(self._obj_id)
+
+ @property
+ def obj_id(self) -> str:
+ if hasattr(self, '_obj_id'):
+ return str(self._obj_id)
+ return ''
+
+ @property
+ def mapper_name(self) -> Optional[str]:
+ if self.dev_path:
+ return f'{storage.get("ENC_IDENTIFIER", "ai")}{self.safe_dev_path.name}'
+ return None
+
+ @property
+ def mapper_path(self) -> Path:
+ if self.mapper_name:
+ return Path(f'/dev/mapper/{self.mapper_name}')
+
+ raise ValueError('No mapper path set')
+
+ @property
+ def safe_dev_path(self) -> Path:
+ if self.dev_path:
+ return self.dev_path
+ raise ValueError('No device path for volume defined')
+
+ @property
+ def safe_fs_type(self) -> FilesystemType:
+ if self.fs_type is None:
+ raise ValueError('File system type is not set')
+ return self.fs_type
+
+ @property
+ def relative_mountpoint(self) -> Path:
+ """
+ Will return the relative path based on the anchor
+ e.g. Path('/mnt/test') -> Path('mnt/test')
+ """
+ if self.mountpoint is not None:
+ return self.mountpoint.relative_to(self.mountpoint.anchor)
+
+ raise ValueError('Mountpoint is not specified')
+
+ @staticmethod
+ def parse_arg(arg: Dict[str, Any]) -> LvmVolume:
+ volume = LvmVolume(
+ status=LvmVolumeStatus(arg['status']),
+ name=arg['name'],
+ fs_type=FilesystemType(arg['fs_type']),
+ length=Size.parse_args(arg['length']),
+ mountpoint=Path(arg['mountpoint']) if arg['mountpoint'] else None,
+ mount_options=arg.get('mount_options', []),
+ btrfs_subvols=SubvolumeModification.parse_args(arg.get('btrfs', []))
+ )
+
+ setattr(volume, '_obj_id', arg['obj_id'])
+
+ return volume
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'obj_id': self.obj_id,
+ 'status': self.status.value,
+ 'name': self.name,
+ 'fs_type': self.fs_type.value,
+ 'length': self.length.json(),
+ 'mountpoint': str(self.mountpoint) if self.mountpoint else None,
+ 'mount_options': self.mount_options,
+ 'btrfs': [vol.json() for vol in self.btrfs_subvols]
+ }
+
+ def table_data(self) -> Dict[str, Any]:
+ part_mod = {
+ 'Type': self.status.value,
+ 'Name': self.name,
+ 'Size': self.length.format_highest(),
+ 'FS type': self.fs_type.value,
+ 'Mountpoint': str(self.mountpoint) if self.mountpoint else '',
+ 'Mount options': ', '.join(self.mount_options),
+ 'Btrfs': '{} {}'.format(str(len(self.btrfs_subvols)), 'vol')
+ }
+ return part_mod
+
+ def is_modify(self) -> bool:
+ return self.status == LvmVolumeStatus.Modify
+
+ def exists(self) -> bool:
+ return self.status == LvmVolumeStatus.Exist
+
+ def is_exists_or_modify(self) -> bool:
+ return self.status in [LvmVolumeStatus.Exist, LvmVolumeStatus.Modify]
+
+ def is_root(self) -> bool:
+ if self.mountpoint is not None:
+ return Path('/') == self.mountpoint
+ else:
+ for subvol in self.btrfs_subvols:
+ if subvol.is_root():
+ return True
+
+ return False
+
+
+@dataclass
+class LvmGroupInfo:
+ vg_size: Size
+ vg_uuid: str
+
+
+@dataclass
+class LvmVolumeInfo:
+ lv_name: str
+ vg_name: str
+ lv_size: Size
+
+
+@dataclass
+class LvmPVInfo:
+ pv_name: Path
+ lv_name: str
+ vg_name: str
+
+
+@dataclass
+class LvmConfiguration:
+ config_type: LvmLayoutType
+ vol_groups: List[LvmVolumeGroup]
+
+ def __post_init__(self):
+ # make sure all volume groups have unique PVs
+ pvs = []
+ for group in self.vol_groups:
+ for pv in group.pvs:
+ if pv in pvs:
+ raise ValueError('A PV cannot be used in multiple volume groups')
+ pvs.append(pv)
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'config_type': self.config_type.value,
+ 'vol_groups': [vol_gr.json() for vol_gr in self.vol_groups]
+ }
+
+ @staticmethod
+ def parse_arg(arg: Dict[str, Any], disk_config: DiskLayoutConfiguration) -> LvmConfiguration:
+ lvm_pvs = []
+ for mod in disk_config.device_modifications:
+ for part in mod.partitions:
+ if part.obj_id in arg.get('lvm_pvs', []):
+ lvm_pvs.append(part)
+
+ return LvmConfiguration(
+ config_type=LvmLayoutType(arg['config_type']),
+ vol_groups=[LvmVolumeGroup.parse_arg(vol_group, disk_config) for vol_group in arg['vol_groups']],
+ )
+
+ def get_all_pvs(self) -> List[PartitionModification]:
+ pvs = []
+ for vg in self.vol_groups:
+ pvs += vg.pvs
+
+ return pvs
+
+ def get_all_volumes(self) -> List[LvmVolume]:
+ volumes = []
+
+ for vg in self.vol_groups:
+ volumes += vg.volumes
+
+ return volumes
+
+ def get_root_volume(self) -> Optional[LvmVolume]:
+ for vg in self.vol_groups:
+ filtered = next(filter(lambda x: x.is_root(), vg.volumes), None)
+ if filtered:
+ return filtered
+
+ return None
+
+
+# def get_lv_crypt_uuid(self, lv: LvmVolume, encryption: EncryptionType) -> str:
+# """
+# Find the LUKS superblock UUID for the device that
+# contains the given logical volume
+# """
+# for vg in self.vol_groups:
+# if vg.contains_lv(lv):
+
+
+@dataclass
+class DeviceModification:
+ device: BDevice
+ wipe: bool
+ partitions: List[PartitionModification] = field(default_factory=list)
+
+ @property
+ def device_path(self) -> Path:
+ return self.device.device_info.path
+
+ def add_partition(self, partition: PartitionModification):
+ self.partitions.append(partition)
+
+ def get_efi_partition(self) -> Optional[PartitionModification]:
+ """
+ Similar to get_boot_partition() but excludes XBOOTLDR partitions from it's candidates.
+ """
+ filtered = filter(lambda x: x.is_efi() and x.mountpoint, self.partitions)
+ return next(filtered, None)
+
+ def get_boot_partition(self) -> Optional[PartitionModification]:
+ """
+ Returns the first partition marked as XBOOTLDR (PARTTYPE id of bc13c2ff-...) or Boot and has a mountpoint.
+ Only returns XBOOTLDR if separate EFI is detected using self.get_efi_partition()
+ Will return None if no suitable partition is found.
+ """
+ if efi_partition := self.get_efi_partition():
+ filtered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions)
+ if boot_partition := next(filtered, None):
+ return boot_partition
+ return efi_partition
+ else:
+ filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions)
+ return next(filtered, None)
+
+ def get_root_partition(self) -> Optional[PartitionModification]:
+ filtered = filter(lambda x: x.is_root(), self.partitions)
+ return next(filtered, None)
+
+ def json(self) -> Dict[str, Any]:
+ """
+ Called when generating configuration files
+ """
+ return {
+ 'device': str(self.device.device_info.path),
+ 'wipe': self.wipe,
+ 'partitions': [p.json() for p in self.partitions]
+ }
+
+
+class EncryptionType(Enum):
+ NoEncryption = "no_encryption"
+ Luks = "luks"
+ LvmOnLuks = 'lvm_on_luks'
+ LuksOnLvm = 'luks_on_lvm'
+
+ @classmethod
+ def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']:
+ return {
+ str(_('No Encryption')): EncryptionType.NoEncryption,
+ str(_('LUKS')): EncryptionType.Luks,
+ str(_('LVM on LUKS')): EncryptionType.LvmOnLuks,
+ str(_('LUKS on LVM')): EncryptionType.LuksOnLvm
+ }
+
+ @classmethod
+ def text_to_type(cls, text: str) -> 'EncryptionType':
+ mapping = cls._encryption_type_mapper()
+ return mapping[text]
+
+ @classmethod
+ def type_to_text(cls, type_: 'EncryptionType') -> str:
+ mapping = cls._encryption_type_mapper()
+ type_to_text = {type_: text for text, type_ in mapping.items()}
+ return type_to_text[type_]
+
+
+@dataclass
+class DiskEncryption:
+ encryption_type: EncryptionType = EncryptionType.NoEncryption
+ encryption_password: str = ''
+ partitions: List[PartitionModification] = field(default_factory=list)
+ lvm_volumes: List[LvmVolume] = field(default_factory=list)
+ hsm_device: Optional[Fido2Device] = None
+
+ def __post_init__(self):
+ if self.encryption_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks] and not self.partitions:
+ raise ValueError('Luks or LvmOnLuks encryption require partitions to be defined')
+
+ if self.encryption_type == EncryptionType.LuksOnLvm and not self.lvm_volumes:
+ raise ValueError('LuksOnLvm encryption require LMV volumes to be defined')
+
+ def should_generate_encryption_file(self, dev: PartitionModification | LvmVolume) -> bool:
+ if isinstance(dev, PartitionModification):
+ return dev in self.partitions and dev.mountpoint != Path('/')
+ elif isinstance(dev, LvmVolume):
+ return dev in self.lvm_volumes and dev.mountpoint != Path('/')
+ return False
+
+ def json(self) -> Dict[str, Any]:
+ obj: Dict[str, Any] = {
+ 'encryption_type': self.encryption_type.value,
+ 'partitions': [p.obj_id for p in self.partitions],
+ 'lvm_volumes': [vol.obj_id for vol in self.lvm_volumes]
+ }
+
+ if self.hsm_device:
+ obj['hsm_device'] = self.hsm_device.json()
+
+ return obj
+
+ @classmethod
+ def validate_enc(cls, disk_config: DiskLayoutConfiguration) -> bool:
+ partitions = []
+
+ for mod in disk_config.device_modifications:
+ for part in mod.partitions:
+ partitions.append(part)
+
+ if len(partitions) > 2: # assume one boot and at least 2 additional
+ if disk_config.lvm_config:
+ return False
+
+ return True
+
+ @classmethod
+ def parse_arg(
+ cls,
+ disk_config: DiskLayoutConfiguration,
+ arg: Dict[str, Any],
+ password: str = ''
+ ) -> Optional['DiskEncryption']:
+ if not cls.validate_enc(disk_config):
+ return None
+
+ enc_partitions = []
+ for mod in disk_config.device_modifications:
+ for part in mod.partitions:
+ if part.obj_id in arg.get('partitions', []):
+ enc_partitions.append(part)
+
+ volumes = []
+ if disk_config.lvm_config:
+ for vol in disk_config.lvm_config.get_all_volumes():
+ if vol.obj_id in arg.get('lvm_volumes', []):
+ volumes.append(vol)
+
+ enc = DiskEncryption(
+ EncryptionType(arg['encryption_type']),
+ password,
+ enc_partitions,
+ volumes
+ )
+
+ if hsm := arg.get('hsm_device', None):
+ enc.hsm_device = Fido2Device.parse_arg(hsm)
+
+ return enc
+
+
+@dataclass
+class Fido2Device:
+ path: Path
+ manufacturer: str
+ product: str
+
+ def json(self) -> Dict[str, str]:
+ return {
+ 'path': str(self.path),
+ 'manufacturer': self.manufacturer,
+ 'product': self.product
+ }
+
+ def table_data(self) -> Dict[str, str]:
+ return {
+ 'Path': str(self.path),
+ 'Manufacturer': self.manufacturer,
+ 'Product': self.product
+ }
+
+ @classmethod
+ def parse_arg(cls, arg: Dict[str, str]) -> 'Fido2Device':
+ return Fido2Device(
+ Path(arg['path']),
+ arg['manufacturer'],
+ arg['product']
+ )
+
+
+@dataclass
+class LsblkInfo:
+ name: str = ''
+ path: Path = Path()
+ pkname: str = ''
+ size: Size = field(default_factory=lambda: Size(0, Unit.B, SectorSize.default()))
+ log_sec: int = 0
+ pttype: str = ''
+ ptuuid: str = ''
+ rota: bool = False
+ tran: Optional[str] = None
+ partn: Optional[int] = None
+ partuuid: Optional[str] = None
+ parttype: Optional[str] = None
+ uuid: Optional[str] = None
+ fstype: Optional[str] = None
+ fsver: Optional[str] = None
+ fsavail: Optional[str] = None
+ fsuse_percentage: Optional[str] = None
+ type: Optional[str] = None
+ mountpoint: Optional[Path] = None
+ mountpoints: List[Path] = field(default_factory=list)
+ fsroots: List[Path] = field(default_factory=list)
+ children: List[LsblkInfo] = field(default_factory=list)
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'name': self.name,
+ 'path': str(self.path),
+ 'pkname': self.pkname,
+ 'size': self.size.format_size(Unit.MiB),
+ 'log_sec': self.log_sec,
+ 'pttype': self.pttype,
+ 'ptuuid': self.ptuuid,
+ 'rota': self.rota,
+ 'tran': self.tran,
+ 'partn': self.partn,
+ 'partuuid': self.partuuid,
+ 'parttype': self.parttype,
+ 'uuid': self.uuid,
+ 'fstype': self.fstype,
+ 'fsver': self.fsver,
+ 'fsavail': self.fsavail,
+ 'fsuse_percentage': self.fsuse_percentage,
+ 'type': self.type,
+ 'mountpoint': self.mountpoint,
+ 'mountpoints': [str(m) for m in self.mountpoints],
+ 'fsroots': [str(r) for r in self.fsroots],
+ 'children': [c.json() for c in self.children]
+ }
+
+ @property
+ def btrfs_subvol_info(self) -> Dict[Path, Path]:
+ """
+ It is assumed that lsblk will contain the fields as
+
+ "mountpoints": ["/mnt/archinstall/log", "/mnt/archinstall/home", "/mnt/archinstall", ...]
+ "fsroots": ["/@log", "/@home", "/@"...]
+
+ we'll thereby map the fsroot, which are the mounted filesystem roots
+ to the corresponding mountpoints
+ """
+ return dict(zip(self.fsroots, self.mountpoints))
+
+ @classmethod
+ def exclude(cls) -> List[str]:
+ return ['children']
+
+ @classmethod
+ def fields(cls) -> List[str]:
+ return [f.name for f in dataclasses.fields(LsblkInfo) if f.name not in cls.exclude()]
+
+ @classmethod
+ def from_json(cls, blockdevice: Dict[str, Any]) -> LsblkInfo:
+ lsblk_info = cls()
+
+ for f in cls.fields():
+ lsblk_field = _clean_field(f, CleanType.Blockdevice)
+ data_field = _clean_field(f, CleanType.Dataclass)
+
+ val: Any = None
+ if isinstance(getattr(lsblk_info, data_field), Path):
+ val = Path(blockdevice[lsblk_field])
+ elif isinstance(getattr(lsblk_info, data_field), Size):
+ sector_size = SectorSize(blockdevice['log-sec'], Unit.B)
+ val = Size(blockdevice[lsblk_field], Unit.B, sector_size)
+ else:
+ val = blockdevice[lsblk_field]
+
+ setattr(lsblk_info, data_field, val)
+
+ lsblk_info.children = [LsblkInfo.from_json(child) for child in blockdevice.get('children', [])]
+
+ # sometimes lsblk returns 'mountpoints': [null]
+ lsblk_info.mountpoints = [Path(mnt) for mnt in lsblk_info.mountpoints if mnt]
+
+ fs_roots = []
+ for r in lsblk_info.fsroots:
+ if r:
+ path = Path(r)
+ # store the fsroot entries without the leading /
+ fs_roots.append(path.relative_to(path.anchor))
+ lsblk_info.fsroots = fs_roots
+
+ return lsblk_info
+
+
+class CleanType(Enum):
+ Blockdevice = auto()
+ Dataclass = auto()
+ Lsblk = auto()
+
+
+def _clean_field(name: str, clean_type: CleanType) -> str:
+ match clean_type:
+ case CleanType.Blockdevice:
+ return name.replace('_percentage', '%').replace('_', '-')
+ case CleanType.Dataclass:
+ return name.lower().replace('-', '_').replace('%', '_percentage')
+ case CleanType.Lsblk:
+ return name.replace('_percentage', '%').replace('_', '-')
+
+
+def _fetch_lsblk_info(
+ dev_path: Optional[Union[Path, str]] = None,
+ reverse: bool = False,
+ full_dev_path: bool = False
+) -> List[LsblkInfo]:
+ fields = [_clean_field(f, CleanType.Lsblk) for f in LsblkInfo.fields()]
+ cmd = ['lsblk', '--json', '--bytes', '--output', '+' + ','.join(fields)]
+
+ if dev_path:
+ cmd.append(str(dev_path))
+
+ if reverse:
+ cmd.append('--inverse')
+
+ if full_dev_path:
+ cmd.append('--paths')
+
+ try:
+ result = SysCommand(cmd).decode()
+ except SysCallError as err:
+ # Get the output minus the message/info from lsblk if it returns a non-zero exit code.
+ if err.worker:
+ err_str = err.worker.decode()
+ debug(f'Error calling lsblk: {err_str}')
+
+ if dev_path:
+ raise DiskError(f'Failed to read disk "{dev_path}" with lsblk')
+
+ raise err
+
+ try:
+ block_devices = json.loads(result)
+ except json.decoder.JSONDecodeError as err:
+ error(f"Could not decode lsblk JSON: {result}")
+ raise err
+
+ blockdevices = block_devices['blockdevices']
+ return [LsblkInfo.from_json(device) for device in blockdevices]
+
+
+def get_lsblk_info(
+ dev_path: Union[Path, str],
+ reverse: bool = False,
+ full_dev_path: bool = False
+) -> LsblkInfo:
+ if infos := _fetch_lsblk_info(dev_path, reverse=reverse, full_dev_path=full_dev_path):
+ return infos[0]
+
+ raise DiskError(f'lsblk failed to retrieve information for "{dev_path}"')
+
+
+def get_all_lsblk_info() -> List[LsblkInfo]:
+ return _fetch_lsblk_info()
+
+
+def get_lsblk_by_mountpoint(mountpoint: Path, as_prefix: bool = False) -> List[LsblkInfo]:
+ def _check(infos: List[LsblkInfo]) -> List[LsblkInfo]:
+ devices = []
+ for entry in infos:
+ if as_prefix:
+ matches = [m for m in entry.mountpoints if str(m).startswith(str(mountpoint))]
+ if matches:
+ devices += [entry]
+ elif mountpoint in entry.mountpoints:
+ devices += [entry]
+
+ if len(entry.children) > 0:
+ if len(match := _check(entry.children)) > 0:
+ devices += match
+
+ return devices
+
+ all_info = get_all_lsblk_info()
+ return _check(all_info)
diff --git a/archinstall/lib/disk/disk_menu.py b/archinstall/lib/disk/disk_menu.py
new file mode 100644
index 00000000..a7d9ccc3
--- /dev/null
+++ b/archinstall/lib/disk/disk_menu.py
@@ -0,0 +1,140 @@
+from typing import Dict, Optional, Any, TYPE_CHECKING, List
+
+from . import DiskLayoutConfiguration, DiskLayoutType
+from .device_model import LvmConfiguration
+from ..disk import (
+ DeviceModification
+)
+from ..interactions import select_disk_config
+from ..interactions.disk_conf import select_lvm_config
+from ..menu import (
+ Selector,
+ AbstractSubMenu
+)
+from ..output import FormattedOutput
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class DiskLayoutConfigurationMenu(AbstractSubMenu):
+ def __init__(
+ self,
+ disk_layout_config: Optional[DiskLayoutConfiguration],
+ data_store: Dict[str, Any],
+ advanced: bool = False
+ ):
+ self._disk_layout_config = disk_layout_config
+ self._advanced = advanced
+
+ super().__init__(data_store=data_store, preview_size=0.5)
+
+ def setup_selection_menu_options(self):
+ self._menu_options['disk_config'] = \
+ Selector(
+ _('Partitioning'),
+ lambda x: self._select_disk_layout_config(x),
+ display_func=lambda x: self._display_disk_layout(x),
+ preview_func=self._prev_disk_layouts,
+ default=self._disk_layout_config,
+ enabled=True
+ )
+ self._menu_options['lvm_config'] = \
+ Selector(
+ _('Logical Volume Management (LVM)'),
+ lambda x: self._select_lvm_config(x),
+ display_func=lambda x: self.defined_text if x else '',
+ preview_func=self._prev_lvm_config,
+ default=self._disk_layout_config.lvm_config if self._disk_layout_config else None,
+ dependencies=[self._check_dep_lvm],
+ enabled=True
+ )
+
+ def run(self, allow_reset: bool = True) -> Optional[DiskLayoutConfiguration]:
+ super().run(allow_reset=allow_reset)
+
+ disk_layout_config: Optional[DiskLayoutConfiguration] = self._data_store.get('disk_config', None)
+
+ if disk_layout_config:
+ disk_layout_config.lvm_config = self._data_store.get('lvm_config', None)
+
+ return disk_layout_config
+
+ def _check_dep_lvm(self) -> bool:
+ disk_layout_conf: Optional[DiskLayoutConfiguration] = self._menu_options['disk_config'].current_selection
+
+ if disk_layout_conf and disk_layout_conf.config_type == DiskLayoutType.Default:
+ return True
+
+ return False
+
+ def _select_disk_layout_config(
+ self,
+ preset: Optional[DiskLayoutConfiguration]
+ ) -> Optional[DiskLayoutConfiguration]:
+ disk_config = select_disk_config(preset, advanced_option=self._advanced)
+
+ if disk_config != preset:
+ self._menu_options['lvm_config'].set_current_selection(None)
+
+ return disk_config
+
+ def _select_lvm_config(self, preset: Optional[LvmConfiguration]) -> Optional[LvmConfiguration]:
+ disk_config: Optional[DiskLayoutConfiguration] = self._menu_options['disk_config'].current_selection
+ if disk_config:
+ return select_lvm_config(disk_config, preset=preset)
+ return preset
+
+ def _display_disk_layout(self, current_value: Optional[DiskLayoutConfiguration] = None) -> str:
+ if current_value:
+ return current_value.config_type.display_msg()
+ return ''
+
+ def _prev_disk_layouts(self) -> Optional[str]:
+ disk_layout_conf: Optional[DiskLayoutConfiguration] = self._menu_options['disk_config'].current_selection
+
+ if disk_layout_conf:
+ device_mods: List[DeviceModification] = \
+ list(filter(lambda x: len(x.partitions) > 0, disk_layout_conf.device_modifications))
+
+ if device_mods:
+ output_partition = '{}: {}\n'.format(str(_('Configuration')), disk_layout_conf.config_type.display_msg())
+ output_btrfs = ''
+
+ for mod in device_mods:
+ # create partition table
+ partition_table = FormattedOutput.as_table(mod.partitions)
+
+ output_partition += f'{mod.device_path}: {mod.device.device_info.model}\n'
+ output_partition += partition_table + '\n'
+
+ # create btrfs table
+ btrfs_partitions = list(
+ filter(lambda p: len(p.btrfs_subvols) > 0, mod.partitions)
+ )
+ for partition in btrfs_partitions:
+ output_btrfs += FormattedOutput.as_table(partition.btrfs_subvols) + '\n'
+
+ output = output_partition + output_btrfs
+ return output.rstrip()
+
+ return None
+
+ def _prev_lvm_config(self) -> Optional[str]:
+ lvm_config: Optional[LvmConfiguration] = self._menu_options['lvm_config'].current_selection
+
+ if lvm_config:
+ output = '{}: {}\n'.format(str(_('Configuration')), lvm_config.config_type.display_msg())
+
+ for vol_gp in lvm_config.vol_groups:
+ pv_table = FormattedOutput.as_table(vol_gp.pvs)
+ output += '{}:\n{}'.format(str(_('Physical volumes')), pv_table)
+
+ output += f'\nVolume Group: {vol_gp.name}'
+
+ lvm_volumes = FormattedOutput.as_table(vol_gp.volumes)
+ output += '\n\n{}:\n{}'.format(str(_('Volumes')), lvm_volumes)
+
+ return output
+
+ return None
diff --git a/archinstall/lib/disk/diskinfo.py b/archinstall/lib/disk/diskinfo.py
deleted file mode 100644
index b56ba282..00000000
--- a/archinstall/lib/disk/diskinfo.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import dataclasses
-import json
-from dataclasses import dataclass, field
-from typing import Optional, List
-
-from ..general import SysCommand
-from ..exceptions import DiskError
-
-@dataclass
-class LsblkInfo:
- size: int = 0
- log_sec: int = 0
- pttype: Optional[str] = None
- rota: bool = False
- tran: Optional[str] = None
- ptuuid: Optional[str] = None
- partuuid: Optional[str] = None
- uuid: Optional[str] = None
- fstype: Optional[str] = None
- type: Optional[str] = None
- mountpoints: List[str] = field(default_factory=list)
-
-
-def get_lsblk_info(dev_path: str) -> LsblkInfo:
- fields = [f.name for f in dataclasses.fields(LsblkInfo)]
- lsblk_fields = ','.join([f.upper().replace('_', '-') for f in fields])
-
- output = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}').decode('UTF-8')
-
- if output:
- block_devices = json.loads(output)
- info = block_devices['blockdevices'][0]
- lsblk_info = LsblkInfo()
-
- for f in fields:
- setattr(lsblk_info, f, info[f.replace('_', '-')])
-
- return lsblk_info
-
- raise DiskError(f'Failed to read disk "{dev_path}" with lsblk')
diff --git a/archinstall/lib/disk/dmcryptdev.py b/archinstall/lib/disk/dmcryptdev.py
deleted file mode 100644
index 63392ffb..00000000
--- a/archinstall/lib/disk/dmcryptdev.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import pathlib
-import logging
-import json
-from dataclasses import dataclass
-from typing import Optional
-from ..exceptions import SysCallError
-from ..general import SysCommand
-from ..output import log
-from .mapperdev import MapperDev
-
-@dataclass
-class DMCryptDev:
- dev_path :pathlib.Path
-
- @property
- def name(self):
- with open(f"/sys/devices/virtual/block/{pathlib.Path(self.path).name}/dm/name", "r") as fh:
- return fh.read().strip()
-
- @property
- def path(self):
- return f"/dev/mapper/{self.dev_path}"
-
- @property
- def blockdev(self):
- pass
-
- @property
- def MapperDev(self):
- return MapperDev(mappername=self.name)
-
- @property
- def mountpoint(self) -> Optional[str]:
- try:
- data = json.loads(SysCommand(f"findmnt --json -R {self.dev_path}").decode())
- for filesystem in data['filesystems']:
- return filesystem.get('target')
-
- except SysCallError as error:
- # Not mounted anywhere most likely
- log(f"Could not locate mount information for {self.dev_path}: {error}", level=logging.WARNING, fg="yellow")
- pass
-
- return None
-
- @property
- def filesystem(self) -> Optional[str]:
- return self.MapperDev.filesystem \ No newline at end of file
diff --git a/archinstall/lib/disk/encryption.py b/archinstall/lib/disk/encryption.py
deleted file mode 100644
index c7496bfa..00000000
--- a/archinstall/lib/disk/encryption.py
+++ /dev/null
@@ -1,174 +0,0 @@
-from typing import Dict, Optional, Any, TYPE_CHECKING, List
-
-from ..menu.abstract_menu import Selector, AbstractSubMenu
-from ..menu.menu import MenuSelectionType
-from ..menu.table_selection_menu import TableMenu
-from ..models.disk_encryption import EncryptionType, DiskEncryption
-from ..user_interaction.partitioning_conf import current_partition_layout
-from ..user_interaction.utils import get_password
-from ..menu import Menu
-from ..general import secret
-from ..hsm.fido import Fido2Device, Fido2
-
-if TYPE_CHECKING:
- _: Any
-
-
-class DiskEncryptionMenu(AbstractSubMenu):
- def __init__(self, data_store: Dict[str, Any], preset: Optional[DiskEncryption], disk_layouts: Dict[str, Any]):
- if preset:
- self._preset = preset
- else:
- self._preset = DiskEncryption()
-
- self._disk_layouts = disk_layouts
- super().__init__(data_store=data_store)
-
- def _setup_selection_menu_options(self):
- self._menu_options['encryption_password'] = \
- Selector(
- _('Encryption password'),
- lambda x: select_encrypted_password(),
- display_func=lambda x: secret(x) if x else '',
- default=self._preset.encryption_password,
- enabled=True
- )
- self._menu_options['encryption_type'] = \
- Selector(
- _('Encryption type'),
- func=lambda preset: select_encryption_type(preset),
- display_func=lambda x: EncryptionType.type_to_text(x) if x else None,
- dependencies=['encryption_password'],
- default=self._preset.encryption_type,
- enabled=True
- )
- self._menu_options['partitions'] = \
- Selector(
- _('Partitions'),
- func=lambda preset: select_partitions_to_encrypt(self._disk_layouts, preset),
- display_func=lambda x: f'{sum([len(y) for y in x.values()])} {_("Partitions")}' if x else None,
- dependencies=['encryption_password'],
- default=self._preset.partitions,
- preview_func=self._prev_disk_layouts,
- enabled=True
- )
- self._menu_options['HSM'] = \
- Selector(
- description=_('Use HSM to unlock encrypted drive'),
- func=lambda preset: select_hsm(preset),
- display_func=lambda x: self._display_hsm(x),
- dependencies=['encryption_password'],
- default=self._preset.hsm_device,
- enabled=True
- )
-
- def run(self, allow_reset: bool = True) -> Optional[DiskEncryption]:
- super().run(allow_reset=allow_reset)
-
- if self._data_store.get('encryption_password', None):
- return DiskEncryption(
- encryption_password=self._data_store.get('encryption_password', None),
- encryption_type=self._data_store['encryption_type'],
- partitions=self._data_store.get('partitions', None),
- hsm_device=self._data_store.get('HSM', None)
- )
-
- return None
-
- def _display_hsm(self, device: Optional[Fido2Device]) -> Optional[str]:
- if device:
- return device.manufacturer
-
- if not Fido2.get_fido2_devices():
- return str(_('No HSM devices available'))
- return None
-
- def _prev_disk_layouts(self) -> Optional[str]:
- selector = self._menu_options['partitions']
- if selector.has_selection():
- partitions: Dict[str, Any] = selector.current_selection
-
- all_partitions = []
- for parts in partitions.values():
- all_partitions += parts
-
- output = str(_('Partitions to be encrypted')) + '\n'
- output += current_partition_layout(all_partitions, with_title=False)
- return output.rstrip()
- return None
-
-
-def select_encryption_type(preset: EncryptionType) -> Optional[EncryptionType]:
- title = str(_('Select disk encryption option'))
- options = [
- # _type_to_text(EncryptionType.FullDiskEncryption),
- EncryptionType.type_to_text(EncryptionType.Partition)
- ]
-
- preset_value = EncryptionType.type_to_text(preset)
- choice = Menu(title, options, preset_values=preset_value).run()
-
- match choice.type_:
- case MenuSelectionType.Reset: return None
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Selection: return EncryptionType.text_to_type(choice.value) # type: ignore
-
-
-def select_encrypted_password() -> Optional[str]:
- if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
- return passwd
- return None
-
-
-def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]:
- title = _('Select a FIDO2 device to use for HSM')
- fido_devices = Fido2.get_fido2_devices()
-
- if fido_devices:
- choice = TableMenu(title, data=fido_devices).run()
- match choice.type_:
- case MenuSelectionType.Reset:
- return None
- case MenuSelectionType.Skip:
- return preset
- case MenuSelectionType.Selection:
- return choice.value # type: ignore
-
- return None
-
-
-def select_partitions_to_encrypt(disk_layouts: Dict[str, Any], preset: Dict[str, Any]) -> Dict[str, Any]:
- # If no partitions was marked as encrypted, but a password was supplied and we have some disks to format..
- # Then we need to identify which partitions to encrypt. This will default to / (root).
- all_partitions = []
- for blockdevice in disk_layouts.values():
- if partitions := blockdevice.get('partitions'):
- partitions = [p for p in partitions if p['mountpoint'] != '/boot']
- all_partitions += partitions
-
- if all_partitions:
- title = str(_('Select which partitions to encrypt'))
- partition_table = current_partition_layout(all_partitions, with_title=False).strip()
-
- choice = TableMenu(
- title,
- table_data=(all_partitions, partition_table),
- multi=True
- ).run()
-
- match choice.type_:
- case MenuSelectionType.Reset:
- return {}
- case MenuSelectionType.Skip:
- return preset
- case MenuSelectionType.Selection:
- selections: List[Any] = choice.value # type: ignore
- partitions = {}
-
- for path, device in disk_layouts.items():
- for part in selections:
- if part in device.get('partitions', []):
- partitions.setdefault(path, []).append(part)
-
- return partitions
- return {}
diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py
new file mode 100644
index 00000000..b0e292ce
--- /dev/null
+++ b/archinstall/lib/disk/encryption_menu.py
@@ -0,0 +1,288 @@
+from pathlib import Path
+from typing import Dict, Optional, Any, TYPE_CHECKING, List
+
+from . import LvmConfiguration, LvmVolume
+from ..disk import (
+ DeviceModification,
+ DiskLayoutConfiguration,
+ PartitionModification,
+ DiskEncryption,
+ EncryptionType
+)
+from ..menu import (
+ Selector,
+ AbstractSubMenu,
+ MenuSelectionType,
+ TableMenu
+)
+from ..interactions.utils import get_password
+from ..menu import Menu
+from ..general import secret
+from .fido import Fido2Device, Fido2
+from ..output import FormattedOutput
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class DiskEncryptionMenu(AbstractSubMenu):
+ def __init__(
+ self,
+ disk_config: DiskLayoutConfiguration,
+ data_store: Dict[str, Any],
+ preset: Optional[DiskEncryption] = None
+ ):
+ if preset:
+ self._preset = preset
+ else:
+ self._preset = DiskEncryption()
+
+ self._disk_config = disk_config
+ super().__init__(data_store=data_store)
+
+ def setup_selection_menu_options(self):
+ self._menu_options['encryption_type'] = \
+ Selector(
+ _('Encryption type'),
+ func=lambda preset: select_encryption_type(self._disk_config, preset),
+ display_func=lambda x: EncryptionType.type_to_text(x) if x else None,
+ default=self._preset.encryption_type,
+ enabled=True,
+ )
+ self._menu_options['encryption_password'] = \
+ Selector(
+ _('Encryption password'),
+ lambda x: select_encrypted_password(),
+ dependencies=[self._check_dep_enc_type],
+ display_func=lambda x: secret(x) if x else '',
+ default=self._preset.encryption_password,
+ enabled=True
+ )
+ self._menu_options['partitions'] = \
+ Selector(
+ _('Partitions'),
+ func=lambda preset: select_partitions_to_encrypt(self._disk_config.device_modifications, preset),
+ display_func=lambda x: f'{len(x)} {_("Partitions")}' if x else None,
+ dependencies=[self._check_dep_partitions],
+ default=self._preset.partitions,
+ preview_func=self._prev_partitions,
+ enabled=True
+ )
+ self._menu_options['lvm_vols'] = \
+ Selector(
+ _('LVM volumes'),
+ func=lambda preset: self._select_lvm_vols(preset),
+ display_func=lambda x: f'{len(x)} {_("LVM volumes")}' if x else None,
+ dependencies=[self._check_dep_lvm_vols],
+ default=self._preset.lvm_volumes,
+ preview_func=self._prev_lvm_vols,
+ enabled=True
+ )
+ self._menu_options['HSM'] = \
+ Selector(
+ description=_('Use HSM to unlock encrypted drive'),
+ func=lambda preset: select_hsm(preset),
+ display_func=lambda x: self._display_hsm(x),
+ preview_func=self._prev_hsm,
+ dependencies=[self._check_dep_enc_type],
+ default=self._preset.hsm_device,
+ enabled=True
+ )
+
+ def _select_lvm_vols(self, preset: List[LvmVolume]) -> List[LvmVolume]:
+ if self._disk_config.lvm_config:
+ return select_lvm_vols_to_encrypt(self._disk_config.lvm_config, preset=preset)
+ return []
+
+ def _check_dep_enc_type(self) -> bool:
+ enc_type: Optional[EncryptionType] = self._menu_options['encryption_type'].current_selection
+ if enc_type and enc_type != EncryptionType.NoEncryption:
+ return True
+ return False
+
+ def _check_dep_partitions(self) -> bool:
+ enc_type: Optional[EncryptionType] = self._menu_options['encryption_type'].current_selection
+ if enc_type and enc_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks]:
+ return True
+ return False
+
+ def _check_dep_lvm_vols(self) -> bool:
+ enc_type: Optional[EncryptionType] = self._menu_options['encryption_type'].current_selection
+ if enc_type and enc_type == EncryptionType.LuksOnLvm:
+ return True
+ return False
+
+ def run(self, allow_reset: bool = True) -> Optional[DiskEncryption]:
+ super().run(allow_reset=allow_reset)
+
+ enc_type = self._data_store.get('encryption_type', None)
+ enc_password = self._data_store.get('encryption_password', None)
+ enc_partitions = self._data_store.get('partitions', None)
+ enc_lvm_vols = self._data_store.get('lvm_vols', None)
+
+ if enc_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks] and enc_partitions:
+ enc_lvm_vols = []
+
+ if enc_type == EncryptionType.LuksOnLvm:
+ enc_partitions = []
+
+ if enc_type != EncryptionType.NoEncryption and enc_password and (enc_partitions or enc_lvm_vols):
+ return DiskEncryption(
+ encryption_password=enc_password,
+ encryption_type=enc_type,
+ partitions=enc_partitions,
+ lvm_volumes=enc_lvm_vols,
+ hsm_device=self._data_store.get('HSM', None)
+ )
+
+ return None
+
+ def _display_hsm(self, device: Optional[Fido2Device]) -> Optional[str]:
+ if device:
+ return device.manufacturer
+
+ return None
+
+ def _prev_partitions(self) -> Optional[str]:
+ partitions: Optional[List[PartitionModification]] = self._menu_options['partitions'].current_selection
+ if partitions:
+ output = str(_('Partitions to be encrypted')) + '\n'
+ output += FormattedOutput.as_table(partitions)
+ return output.rstrip()
+
+ return None
+
+ def _prev_lvm_vols(self) -> Optional[str]:
+ volumes: Optional[List[PartitionModification]] = self._menu_options['lvm_vols'].current_selection
+ if volumes:
+ output = str(_('LVM volumes to be encrypted')) + '\n'
+ output += FormattedOutput.as_table(volumes)
+ return output.rstrip()
+
+ return None
+
+ def _prev_hsm(self) -> Optional[str]:
+ try:
+ Fido2.get_fido2_devices()
+ except ValueError:
+ return str(_('Unable to determine fido2 devices. Is libfido2 installed?'))
+
+ fido_device: Optional[Fido2Device] = self._menu_options['HSM'].current_selection
+
+ if fido_device:
+ output = '{}: {}'.format(str(_('Path')), fido_device.path)
+ output += '{}: {}'.format(str(_('Manufacturer')), fido_device.manufacturer)
+ output += '{}: {}'.format(str(_('Product')), fido_device.product)
+ return output
+
+ return None
+
+
+def select_encryption_type(disk_config: DiskLayoutConfiguration, preset: EncryptionType) -> Optional[EncryptionType]:
+ title = str(_('Select disk encryption option'))
+
+ if disk_config.lvm_config:
+ options = [
+ EncryptionType.type_to_text(EncryptionType.LvmOnLuks),
+ EncryptionType.type_to_text(EncryptionType.LuksOnLvm)
+ ]
+ else:
+ options = [EncryptionType.type_to_text(EncryptionType.Luks)]
+
+ preset_value = EncryptionType.type_to_text(preset)
+
+ choice = Menu(title, options, preset_values=preset_value).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset: return None
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return EncryptionType.text_to_type(choice.value) # type: ignore
+
+
+def select_encrypted_password() -> Optional[str]:
+ if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
+ return passwd
+ return None
+
+
+def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]:
+ title = _('Select a FIDO2 device to use for HSM')
+
+ try:
+ fido_devices = Fido2.get_fido2_devices()
+ except ValueError:
+ return None
+
+ if fido_devices:
+ choice = TableMenu(title, data=fido_devices).run()
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return None
+ case MenuSelectionType.Skip:
+ return preset
+ case MenuSelectionType.Selection:
+ return choice.value # type: ignore
+
+ return None
+
+
+def select_partitions_to_encrypt(
+ modification: List[DeviceModification],
+ preset: List[PartitionModification]
+) -> List[PartitionModification]:
+ partitions: List[PartitionModification] = []
+
+ # do not allow encrypting the boot partition
+ for mod in modification:
+ partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions))
+
+ # do not allow encrypting existing partitions that are not marked as wipe
+ avail_partitions = list(filter(lambda x: not x.exists(), partitions))
+
+ if avail_partitions:
+ title = str(_('Select which partitions to encrypt'))
+ partition_table = FormattedOutput.as_table(avail_partitions)
+
+ choice = TableMenu(
+ title,
+ table_data=(avail_partitions, partition_table),
+ preset=preset,
+ multi=True
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return []
+ case MenuSelectionType.Skip:
+ return preset
+ case MenuSelectionType.Selection:
+ return choice.multi_value
+ return []
+
+
+def select_lvm_vols_to_encrypt(
+ lvm_config: LvmConfiguration,
+ preset: List[LvmVolume]
+) -> List[LvmVolume]:
+ volumes: List[LvmVolume] = lvm_config.get_all_volumes()
+
+ if volumes:
+ title = str(_('Select which LVM volumes to encrypt'))
+ partition_table = FormattedOutput.as_table(volumes)
+
+ choice = TableMenu(
+ title,
+ table_data=(volumes, partition_table),
+ preset=preset,
+ multi=True
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return []
+ case MenuSelectionType.Skip:
+ return preset
+ case MenuSelectionType.Selection:
+ return choice.multi_value
+
+ return []
diff --git a/archinstall/lib/hsm/fido.py b/archinstall/lib/disk/fido.py
index 1c226322..5a139534 100644
--- a/archinstall/lib/hsm/fido.py
+++ b/archinstall/lib/disk/fido.py
@@ -1,37 +1,13 @@
from __future__ import annotations
import getpass
-import logging
-
-from dataclasses import dataclass
from pathlib import Path
-from typing import List, Dict
+from typing import List
+from .device_model import Fido2Device
from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes
-from ..disk.partition import Partition
-from ..general import log
-
-
-@dataclass
-class Fido2Device:
- path: Path
- manufacturer: str
- product: str
-
- def json(self) -> Dict[str, str]:
- return {
- 'path': str(self.path),
- 'manufacturer': self.manufacturer,
- 'product': self.product
- }
-
- @classmethod
- def parse_arg(cls, arg: Dict[str, str]) -> 'Fido2Device':
- return Fido2Device(
- Path(arg['path']),
- arg['manufacturer'],
- arg['product']
- )
+from ..output import error, info
+from ..exceptions import SysCallError
class Fido2:
@@ -58,15 +34,16 @@ class Fido2:
/dev/hidraw1 Yubico YubiKey OTP+FIDO+CCID
"""
- # to prevent continous reloading which will slow
+ # to prevent continuous reloading which will slow
# down moving the cursor in the menu
if not cls._loaded or reload:
- ret = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8')
- if not ret:
- log('Unable to retrieve fido2 devices', level=logging.ERROR)
- return []
+ try:
+ ret = SysCommand("systemd-cryptenroll --fido2-device=list").decode()
+ except SysCallError:
+ error('fido2 support is most likely not installed')
+ raise ValueError('HSM devices can not be detected, is libfido2 installed?')
- fido_devices = clear_vt100_escape_codes(ret)
+ fido_devices: str = clear_vt100_escape_codes(ret) # type: ignore
manufacturer_pos = 0
product_pos = 0
@@ -83,7 +60,7 @@ class Fido2:
product = line[product_pos:]
devices.append(
- Fido2Device(path, manufacturer, product)
+ Fido2Device(Path(path), manufacturer, product)
)
cls._loaded = True
@@ -92,18 +69,24 @@ class Fido2:
return cls._fido2_devices
@classmethod
- def fido2_enroll(cls, hsm_device: Fido2Device, partition :Partition, password :str):
- worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device.path} {partition.real_device}", peek_output=True)
+ def fido2_enroll(
+ cls,
+ hsm_device: Fido2Device,
+ dev_path: Path,
+ password: str
+ ):
+ worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device.path} {dev_path}", peek_output=True)
pw_inputted = False
pin_inputted = False
while worker.is_alive():
- if pw_inputted is False and bytes(f"please enter current passphrase for disk {partition.real_device}", 'UTF-8') in worker._trace_log.lower():
- worker.write(bytes(password, 'UTF-8'))
- pw_inputted = True
-
- elif pin_inputted is False and bytes(f"please enter security token pin", 'UTF-8') in worker._trace_log.lower():
- worker.write(bytes(getpass.getpass(" "), 'UTF-8'))
- pin_inputted = True
-
- log(f"You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds.", level=logging.INFO, fg="yellow")
+ if pw_inputted is False:
+ if bytes(f"please enter current passphrase for disk {dev_path}", 'UTF-8') in worker._trace_log.lower():
+ worker.write(bytes(password, 'UTF-8'))
+ pw_inputted = True
+ elif pin_inputted is False:
+ if bytes(f"please enter security token pin", 'UTF-8') in worker._trace_log.lower():
+ worker.write(bytes(getpass.getpass(" "), 'UTF-8'))
+ pin_inputted = True
+
+ info('You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds')
diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py
index 1083df53..5c11896e 100644
--- a/archinstall/lib/disk/filesystem.py
+++ b/archinstall/lib/disk/filesystem.py
@@ -1,301 +1,381 @@
from __future__ import annotations
+
+import signal
+import sys
import time
-import logging
-import json
-import pathlib
-from typing import Optional, Dict, Any, TYPE_CHECKING
-# https://stackoverflow.com/a/39757388/929999
-from ..models.disk_encryption import DiskEncryption
+from pathlib import Path
+from typing import Any, Optional, TYPE_CHECKING, List, Dict, Set
+
+from .device_handler import device_handler
+from .device_model import (
+ DiskLayoutConfiguration, DiskLayoutType, PartitionTable,
+ FilesystemType, DiskEncryption, LvmVolumeGroup,
+ Size, Unit, SectorSize, PartitionModification, EncryptionType,
+ LvmVolume, LvmConfiguration
+)
+from ..hardware import SysInfo
+from ..luks import Luks2
+from ..menu import Menu
+from ..output import debug, info
+from ..general import SysCommand
if TYPE_CHECKING:
- from .blockdevice import BlockDevice
_: Any
-from .partition import Partition
-from .validators import valid_fs_type
-from ..exceptions import DiskError, SysCallError
-from ..general import SysCommand
-from ..output import log
-from ..storage import storage
-GPT = 0b00000001
-MBR = 0b00000010
+class FilesystemHandler:
+ def __init__(
+ self,
+ disk_config: DiskLayoutConfiguration,
+ enc_conf: Optional[DiskEncryption] = None
+ ):
+ self._disk_config = disk_config
+ self._enc_config = enc_conf
+
+ def perform_filesystem_operations(self, show_countdown: bool = True):
+ if self._disk_config.config_type == DiskLayoutType.Pre_mount:
+ debug('Disk layout configuration is set to pre-mount, not performing any operations')
+ return
+
+ device_mods = list(filter(lambda x: len(x.partitions) > 0, self._disk_config.device_modifications))
+
+ if not device_mods:
+ debug('No modifications required')
+ return
+
+ device_paths = ', '.join([str(mod.device.device_info.path) for mod in device_mods])
+
+ # Issue a final warning before we continue with something un-revertable.
+ # We mention the drive one last time, and count from 5 to 0.
+ print(str(_(' ! Formatting {} in ')).format(device_paths))
+
+ if show_countdown:
+ self._do_countdown()
+
+ # Setup the blockdevice, filesystem (and optionally encryption).
+ # Once that's done, we'll hand over to perform_installation()
+ partition_table = PartitionTable.GPT
+ if SysInfo.has_uefi() is False:
+ partition_table = PartitionTable.MBR
+
+ for mod in device_mods:
+ device_handler.partition(mod, partition_table=partition_table)
+
+ if self._disk_config.lvm_config:
+ for mod in device_mods:
+ if boot_part := mod.get_boot_partition():
+ debug(f'Formatting boot partition: {boot_part.dev_path}')
+ self._format_partitions(
+ [boot_part],
+ mod.device_path
+ )
+
+ self.perform_lvm_operations()
+ else:
+ for mod in device_mods:
+ self._format_partitions(
+ mod.partitions,
+ mod.device_path
+ )
-# A sane default is 5MiB, that allows for plenty of buffer for GRUB on MBR
-# but also 4MiB for memory cards for instance. And another 1MiB to avoid issues.
-# (we've been pestered by disk issues since the start, so please let this be here for a few versions)
-DEFAULT_PARTITION_START = '5MiB'
+ for part_mod in mod.partitions:
+ if part_mod.fs_type == FilesystemType.Btrfs:
+ device_handler.create_btrfs_volumes(part_mod, enc_conf=self._enc_config)
-class Filesystem:
- # TODO:
- # When instance of a HDD is selected, check all usages and gracefully unmount them
- # as well as close any crypto handles.
- def __init__(self, blockdevice :BlockDevice, mode :int):
- self.blockdevice = blockdevice
- self.mode = mode
+ def _format_partitions(
+ self,
+ partitions: List[PartitionModification],
+ device_path: Path
+ ):
+ """
+ Format can be given an overriding path, for instance /dev/null to test
+ the formatting functionality and in essence the support for the given filesystem.
+ """
- def __enter__(self, *args :str, **kwargs :str) -> 'Filesystem':
- return self
+ # don't touch existing partitions
+ create_or_modify_parts = [p for p in partitions if p.is_create_or_modify()]
- def __repr__(self) -> str:
- return f"Filesystem(blockdevice={self.blockdevice}, mode={self.mode})"
+ self._validate_partitions(create_or_modify_parts)
- def __exit__(self, *args :str, **kwargs :str) -> bool:
- # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
- if len(args) >= 2 and args[1]:
- raise args[1]
+ # make sure all devices are unmounted
+ device_handler.umount_all_existing(device_path)
- SysCommand('sync')
- return True
+ for part_mod in create_or_modify_parts:
+ # partition will be encrypted
+ if self._enc_config is not None and part_mod in self._enc_config.partitions:
+ device_handler.format_encrypted(
+ part_mod.safe_dev_path,
+ part_mod.mapper_name,
+ part_mod.safe_fs_type,
+ self._enc_config
+ )
+ else:
+ device_handler.format(part_mod.safe_fs_type, part_mod.safe_dev_path)
+
+ # synchronize with udev before using lsblk
+ SysCommand('udevadm settle')
+
+ lsblk_info = device_handler.fetch_part_info(part_mod.safe_dev_path)
+
+ part_mod.partn = lsblk_info.partn
+ part_mod.partuuid = lsblk_info.partuuid
+ part_mod.uuid = lsblk_info.uuid
+
+ def _validate_partitions(self, partitions: List[PartitionModification]):
+ checks = {
+ # verify that all partitions have a path set (which implies that they have been created)
+ lambda x: x.dev_path is None: ValueError('When formatting, all partitions must have a path set'),
+ # crypto luks is not a valid file system type
+ lambda x: x.fs_type is FilesystemType.Crypto_luks: ValueError(
+ 'Crypto luks cannot be set as a filesystem type'),
+ # file system type must be set
+ lambda x: x.fs_type is None: ValueError('File system type must be set for modification')
+ }
+
+ for check, exc in checks.items():
+ found = next(filter(check, partitions), None)
+ if found is not None:
+ raise exc
+
+ def perform_lvm_operations(self):
+ info('Setting up LVM config...')
+
+ if not self._disk_config.lvm_config:
+ return
+
+ if self._enc_config:
+ self._setup_lvm_encrypted(
+ self._disk_config.lvm_config,
+ self._enc_config
+ )
+ else:
+ self._setup_lvm(self._disk_config.lvm_config)
+ self._format_lvm_vols(self._disk_config.lvm_config)
+
+ def _setup_lvm_encrypted(self, lvm_config: LvmConfiguration, enc_config: DiskEncryption):
+ if enc_config.encryption_type == EncryptionType.LvmOnLuks:
+ enc_mods = self._encrypt_partitions(enc_config, lock_after_create=False)
+
+ self._setup_lvm(lvm_config, enc_mods)
+ self._format_lvm_vols(lvm_config)
+
+ # export the lvm group safely otherwise the Luks cannot be closed
+ self._safely_close_lvm(lvm_config)
+
+ for luks in enc_mods.values():
+ luks.lock()
+ elif enc_config.encryption_type == EncryptionType.LuksOnLvm:
+ self._setup_lvm(lvm_config)
+ enc_vols = self._encrypt_lvm_vols(lvm_config, enc_config, False)
+ self._format_lvm_vols(lvm_config, enc_vols)
+
+ for luks in enc_vols.values():
+ luks.lock()
+
+ self._safely_close_lvm(lvm_config)
+
+ def _safely_close_lvm(self, lvm_config: LvmConfiguration):
+ for vg in lvm_config.vol_groups:
+ for vol in vg.volumes:
+ device_handler.lvm_vol_change(vol, False)
+
+ device_handler.lvm_export_vg(vg)
+
+ def _setup_lvm(
+ self,
+ lvm_config: LvmConfiguration,
+ enc_mods: Dict[PartitionModification, Luks2] = {}
+ ):
+ self._lvm_create_pvs(lvm_config, enc_mods)
+
+ for vg in lvm_config.vol_groups:
+ pv_dev_paths = self._get_all_pv_dev_paths(vg.pvs, enc_mods)
+
+ device_handler.lvm_vg_create(pv_dev_paths, vg.name)
+
+ # figure out what the actual available size in the group is
+ vg_info = device_handler.lvm_group_info(vg.name)
+
+ if not vg_info:
+ raise ValueError('Unable to fetch VG info')
+
+ # the actual available LVM Group size will be smaller than the
+ # total PVs size due to reserved metadata storage etc.
+ # so we'll have a look at the total avail. size, check the delta
+ # to the desired sizes and subtract some equally from the actually
+ # created volume
+ avail_size = vg_info.vg_size
+ desired_size = sum([vol.length for vol in vg.volumes], Size(0, Unit.B, SectorSize.default()))
+
+ delta = desired_size - avail_size
+ max_vol_offset = delta.convert(Unit.B)
+
+ max_vol = max(vg.volumes, key=lambda x: x.length)
+
+ for lv in vg.volumes:
+ offset = max_vol_offset if lv == max_vol else None
+
+ debug(f'vg: {vg.name}, vol: {lv.name}, offset: {offset}')
+ device_handler.lvm_vol_create(vg.name, lv, offset)
- def partuuid_to_index(self, uuid :str) -> Optional[int]:
- for i in range(storage['DISK_RETRY_ATTEMPTS']):
- self.partprobe()
- time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i))
-
- # We'll use unreliable lbslk to grab children under the /dev/<device>
- output = json.loads(SysCommand(f"lsblk --json {self.blockdevice.device}").decode('UTF-8'))
-
- for device in output['blockdevices']:
- for index, partition in enumerate(device.get('children', [])):
- # But we'll use blkid to reliably grab the PARTUUID for that child device (partition)
- partition_uuid = SysCommand(f"blkid -s PARTUUID -o value /dev/{partition.get('name')}").decode().strip()
- if partition_uuid.lower() == uuid.lower():
- return index
-
- raise DiskError(f"Failed to convert PARTUUID {uuid} to a partition index number on blockdevice {self.blockdevice.device}")
-
- def load_layout(self, layout :Dict[str, Any]) -> None:
- from ..luks import luks2
- from .btrfs import BTRFSPartition
-
- # If the layout tells us to wipe the drive, we do so
- if layout.get('wipe', False):
- if self.mode == GPT:
- if not self.parted_mklabel(self.blockdevice.device, "gpt"):
- raise KeyError(f"Could not create a GPT label on {self}")
- elif self.mode == MBR:
- if not self.parted_mklabel(self.blockdevice.device, "msdos"):
- raise KeyError(f"Could not create a MS-DOS label on {self}")
-
- self.blockdevice.flush_cache()
- time.sleep(3)
-
- prev_partition = None
- # We then iterate the partitions in order
- for partition in layout.get('partitions', []):
- # We don't want to re-add an existing partition (those containing a UUID already)
- if partition.get('wipe', False) and not partition.get('PARTUUID', None):
- start = partition.get('start') or (
- prev_partition and f'{prev_partition["device_instance"].end_sectors}s' or DEFAULT_PARTITION_START)
- partition['device_instance'] = self.add_partition(partition.get('type', 'primary'),
- start=start,
- end=partition.get('size', '100%'),
- partition_format=partition.get('filesystem', {}).get('format', 'btrfs'),
- skip_mklabel=layout.get('wipe', False) is not False)
-
- elif (partition_uuid := partition.get('PARTUUID')):
- # We try to deal with both UUID and PARTUUID of a partition when it's being re-used.
- # We should re-name or separate this logi based on partition.get('PARTUUID') and partition.get('UUID')
- # but for now, lets just attempt to deal with both.
- try:
- partition['device_instance'] = self.blockdevice.get_partition(uuid=partition_uuid)
- except DiskError:
- partition['device_instance'] = self.blockdevice.get_partition(partuuid=partition_uuid)
-
- log(_("Re-using partition instance: {}").format(partition['device_instance']), level=logging.DEBUG, fg="gray")
+ while True:
+ debug('Fetching LVM volume info')
+ lv_info = device_handler.lvm_vol_info(lv.name)
+ if lv_info is not None:
+ break
+
+ time.sleep(1)
+
+ self._lvm_vol_handle_e2scrub(vg)
+
+ def _format_lvm_vols(
+ self,
+ lvm_config: LvmConfiguration,
+ enc_vols: Dict[LvmVolume, Luks2] = {}
+ ):
+ for vol in lvm_config.get_all_volumes():
+ if enc_vol := enc_vols.get(vol, None):
+ if not enc_vol.mapper_dev:
+ raise ValueError('No mapper device defined')
+ path = enc_vol.mapper_dev
else:
- log(f"{self}.load_layout() doesn't know how to work without 'wipe' being set or UUID ({partition.get('PARTUUID')}) was given and found.", fg="yellow", level=logging.WARNING)
- continue
-
- if partition.get('filesystem', {}).get('format', False):
- # needed for backward compatibility with the introduction of the new "format_options"
- format_options = partition.get('options',[]) + partition.get('filesystem',{}).get('format_options',[])
- disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
-
- if disk_encryption and partition in disk_encryption.all_partitions:
- if not partition['device_instance']:
- raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!")
-
- if partition.get('mountpoint',None):
- loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['mountpoint']).name}loop"
- else:
- loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}"
-
- partition['device_instance'].encrypt(password=disk_encryption.encryption_password)
- # Immediately unlock the encrypted device to format the inner volume
- with luks2(partition['device_instance'], loopdev, disk_encryption.encryption_password, auto_unmount=True) as unlocked_device:
- if not partition.get('wipe'):
- if storage['arguments'] == 'silent':
- raise ValueError(f"Missing fs-type to format on newly created encrypted partition {partition['device_instance']}")
- else:
- if not partition.get('filesystem'):
- partition['filesystem'] = {}
-
- if not partition['filesystem'].get('format', False):
- while True:
- partition['filesystem']['format'] = input(f"Enter a valid fs-type for newly encrypted partition {partition['filesystem']['format']}: ").strip()
- if not partition['filesystem']['format'] or valid_fs_type(partition['filesystem']['format']) is False:
- log(_("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."))
- continue
- break
-
- unlocked_device.format(partition['filesystem']['format'], options=format_options)
-
- elif partition.get('wipe', False):
- if not partition['device_instance']:
- raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!")
-
- partition['device_instance'].format(partition['filesystem']['format'], options=format_options)
-
- if partition['filesystem']['format'] == 'btrfs':
- # We upgrade the device instance to a BTRFSPartition if we format it as such.
- # This is so that we can gain access to more features than otherwise available in Partition()
- partition['device_instance'] = BTRFSPartition(
- partition['device_instance'].path,
- block_device=partition['device_instance'].block_device,
- encrypted=False,
- filesystem='btrfs',
- autodetect_filesystem=False
- )
-
- if partition.get('boot', False):
- log(f"Marking partition {partition['device_instance']} as bootable.")
- self.set(self.partuuid_to_index(partition['device_instance'].part_uuid), 'boot on')
-
- prev_partition = partition
-
- def find_partition(self, mountpoint :str) -> Partition:
- for partition in self.blockdevice:
- if partition.target_mountpoint == mountpoint or partition.mountpoint == mountpoint:
- return partition
-
- def partprobe(self) -> bool:
- try:
- SysCommand(f'partprobe {self.blockdevice.device}')
- except SysCallError as error:
- log(f"Could not execute partprobe: {error!r}", level=logging.ERROR, fg="red")
- raise DiskError(f"Could not run partprobe on {self.blockdevice.device}: {error!r}")
+ path = vol.safe_dev_path
- return True
+ # wait a bit otherwise the mkfs will fail as it can't
+ # find the mapper device yet
+ device_handler.format(vol.fs_type, path)
- def raw_parted(self, string: str) -> SysCommand:
- try:
- cmd_handle = SysCommand(f'/usr/bin/parted -s {string}')
- time.sleep(0.5)
- return cmd_handle
- except SysCallError as error:
- log(f"Parted ended with a bad exit code: {error.exit_code} ({error})", level=logging.ERROR, fg="red")
- return error
+ if vol.fs_type == FilesystemType.Btrfs:
+ device_handler.create_lvm_btrfs_subvolumes(path, vol.btrfs_subvols, vol.mount_options)
- def parted(self, string: str) -> bool:
- """
- Performs a parted execution of the given string
+ def _lvm_create_pvs(
+ self,
+ lvm_config: LvmConfiguration,
+ enc_mods: Dict[PartitionModification, Luks2] = {}
+ ):
+ pv_paths: Set[Path] = set()
- :param string: A raw string passed to /usr/bin/parted -s <string>
- :type string: str
- """
- if (parted_handle := self.raw_parted(string)).exit_code == 0:
- return self.partprobe()
- else:
- raise DiskError(f"Parted failed to add a partition: {parted_handle}")
+ for vg in lvm_config.vol_groups:
+ pv_paths |= self._get_all_pv_dev_paths(vg.pvs, enc_mods)
- def use_entire_disk(self, root_filesystem_type :str = 'ext4') -> Partition:
- # TODO: Implement this with declarative profiles instead.
- raise ValueError("Installation().use_entire_disk() has to be re-worked.")
+ device_handler.lvm_pv_create(pv_paths)
- def add_partition(
+ def _get_all_pv_dev_paths(
self,
- partition_type :str,
- start :str,
- end :str,
- partition_format :Optional[str] = None,
- skip_mklabel :bool = False
- ) -> Partition:
- log(f'Adding partition to {self.blockdevice}, {start}->{end}', level=logging.INFO)
-
- if len(self.blockdevice.partitions) == 0 and skip_mklabel is False:
- # If it's a completely empty drive, and we're about to add partitions to it
- # we need to make sure there's a filesystem label.
- if self.mode == GPT:
- if not self.parted_mklabel(self.blockdevice.device, "gpt"):
- raise KeyError(f"Could not create a GPT label on {self}")
- elif self.mode == MBR:
- if not self.parted_mklabel(self.blockdevice.device, "msdos"):
- raise KeyError(f"Could not create a MS-DOS label on {self}")
-
- self.blockdevice.flush_cache()
-
- previous_partuuids = []
- for partition in self.blockdevice.partitions.values():
- try:
- previous_partuuids.append(partition.part_uuid)
- except DiskError:
- pass
-
- # TODO this check should probably run in the setup process rather than during the installation
- if self.mode == MBR:
- if len(self.blockdevice.partitions) > 3:
- DiskError("Too many partitions on disk, MBR disks can only have 3 primary partitions")
-
- if partition_format:
- parted_string = f'{self.blockdevice.device} mkpart {partition_type} {partition_format} {start} {end}'
- else:
- parted_string = f'{self.blockdevice.device} mkpart {partition_type} {start} {end}'
-
- log(f"Adding partition using the following parted command: {parted_string}", level=logging.DEBUG)
-
- if self.parted(parted_string):
- for count in range(storage.get('DISK_RETRY_ATTEMPTS', 3)):
- self.blockdevice.flush_cache()
-
- new_partition_uuids = [partition.part_uuid for partition in self.blockdevice.partitions.values()]
- new_partuuid_set = (set(previous_partuuids) ^ set(new_partition_uuids))
-
- if len(new_partuuid_set) and (new_partuuid := new_partuuid_set.pop()):
- try:
- return self.blockdevice.get_partition(partuuid=new_partuuid)
- except Exception as err:
- log(f'Blockdevice: {self.blockdevice}', level=logging.ERROR, fg="red")
- log(f'Partitions: {self.blockdevice.partitions}', level=logging.ERROR, fg="red")
- log(f'Partition set: {new_partuuid_set}', level=logging.ERROR, fg="red")
- log(f'New PARTUUID: {[new_partuuid]}', level=logging.ERROR, fg="red")
- log(f'get_partition(): {self.blockdevice.get_partition}', level=logging.ERROR, fg="red")
- raise err
- else:
- log(f"Could not get UUID for partition. Waiting {storage.get('DISK_TIMEOUTS', 1) * count}s before retrying.",level=logging.DEBUG)
- self.partprobe()
- time.sleep(max(0.1, storage.get('DISK_TIMEOUTS', 1)))
- else:
- print("Parted did not return True during partition creation")
+ pvs: List[PartitionModification],
+ enc_mods: Dict[PartitionModification, Luks2] = {}
+ ) -> Set[Path]:
+ pv_paths: Set[Path] = set()
+
+ for pv in pvs:
+ if enc_pv := enc_mods.get(pv, None):
+ if mapper := enc_pv.mapper_dev:
+ pv_paths.add(mapper)
+ else:
+ pv_paths.add(pv.safe_dev_path)
+
+ return pv_paths
+
+ def _encrypt_lvm_vols(
+ self,
+ lvm_config: LvmConfiguration,
+ enc_config: DiskEncryption,
+ lock_after_create: bool = True
+ ) -> Dict[LvmVolume, Luks2]:
+ enc_vols: Dict[LvmVolume, Luks2] = {}
+
+ for vol in lvm_config.get_all_volumes():
+ if vol in enc_config.lvm_volumes:
+ luks_handler = device_handler.encrypt(
+ vol.safe_dev_path,
+ vol.mapper_name,
+ enc_config.encryption_password,
+ lock_after_create
+ )
+
+ enc_vols[vol] = luks_handler
+
+ return enc_vols
+
+ def _encrypt_partitions(
+ self,
+ enc_config: DiskEncryption,
+ lock_after_create: bool = True
+ ) -> Dict[PartitionModification, Luks2]:
+ enc_mods: Dict[PartitionModification, Luks2] = {}
+
+ for mod in self._disk_config.device_modifications:
+ partitions = mod.partitions
+
+ # don't touch existing partitions
+ filtered_part = [p for p in partitions if not p.exists()]
+
+ self._validate_partitions(filtered_part)
+
+ # make sure all devices are unmounted
+ device_handler.umount_all_existing(mod.device_path)
+
+ enc_mods = {}
+
+ for part_mod in filtered_part:
+ if part_mod in enc_config.partitions:
+ luks_handler = device_handler.encrypt(
+ part_mod.safe_dev_path,
+ part_mod.mapper_name,
+ enc_config.encryption_password,
+ lock_after_create=lock_after_create
+ )
+
+ enc_mods[part_mod] = luks_handler
- total_partitions = set([partition.part_uuid for partition in self.blockdevice.partitions.values()])
- total_partitions.update(previous_partuuids)
+ return enc_mods
- # TODO: This should never be able to happen
- log(f"Could not find the new PARTUUID after adding the partition.", level=logging.ERROR, fg="red")
- log(f"Previous partitions: {previous_partuuids}", level=logging.ERROR, fg="red")
- log(f"New partitions: {total_partitions}", level=logging.ERROR, fg="red")
+ def _lvm_vol_handle_e2scrub(self, vol_gp: LvmVolumeGroup):
+ # from arch wiki:
+ # If a logical volume will be formatted with ext4, leave at least 256 MiB
+ # free space in the volume group to allow using e2scrub
+ if any([vol.fs_type == FilesystemType.Ext4 for vol in vol_gp.volumes]):
+ largest_vol = max(vol_gp.volumes, key=lambda x: x.length)
- raise DiskError(f"Could not add partition using: {parted_string}")
+ device_handler.lvm_vol_reduce(
+ largest_vol.safe_dev_path,
+ Size(256, Unit.MiB, SectorSize.default())
+ )
- def set_name(self, partition: int, name: str) -> bool:
- return self.parted(f'{self.blockdevice.device} name {partition + 1} "{name}"') == 0
+ def _do_countdown(self) -> bool:
+ SIG_TRIGGER = False
- def set(self, partition: int, string: str) -> bool:
- log(f"Setting {string} on (parted) partition index {partition+1}", level=logging.INFO)
- return self.parted(f'{self.blockdevice.device} set {partition + 1} {string}') == 0
+ def kill_handler(sig: int, frame: Any) -> None:
+ print()
+ exit(0)
- def parted_mklabel(self, device: str, disk_label: str) -> bool:
- log(f"Creating a new partition label on {device}", level=logging.INFO, fg="yellow")
- # Try to unmount devices before attempting to run mklabel
- try:
- SysCommand(f'bash -c "umount {device}?"')
- except:
- pass
+ def sig_handler(sig: int, frame: Any) -> None:
+ signal.signal(signal.SIGINT, kill_handler)
- self.partprobe()
- worked = self.raw_parted(f'{device} mklabel {disk_label}').exit_code == 0
- self.partprobe()
+ original_sigint_handler = signal.getsignal(signal.SIGINT)
+ signal.signal(signal.SIGINT, sig_handler)
- return worked
+ for i in range(5, 0, -1):
+ print(f"{i}", end='')
+
+ for x in range(4):
+ sys.stdout.flush()
+ time.sleep(0.25)
+ print(".", end='')
+
+ if SIG_TRIGGER:
+ prompt = _('Do you really want to abort?')
+ choice = Menu(prompt, Menu.yes_no(), skip=False).run()
+ if choice.value == Menu.yes():
+ exit(0)
+
+ if SIG_TRIGGER is False:
+ sys.stdin.read()
+
+ SIG_TRIGGER = False
+ signal.signal(signal.SIGINT, sig_handler)
+
+ print()
+ signal.signal(signal.SIGINT, original_sigint_handler)
+
+ return True
diff --git a/archinstall/lib/disk/helpers.py b/archinstall/lib/disk/helpers.py
deleted file mode 100644
index 80d0cb53..00000000
--- a/archinstall/lib/disk/helpers.py
+++ /dev/null
@@ -1,556 +0,0 @@
-from __future__ import annotations
-import json
-import logging
-import os # type: ignore
-import pathlib
-import re
-import time
-import glob
-
-from typing import Union, List, Iterator, Dict, Optional, Any, TYPE_CHECKING
-# https://stackoverflow.com/a/39757388/929999
-from .diskinfo import get_lsblk_info
-from ..models.subvolume import Subvolume
-
-from .blockdevice import BlockDevice
-from .dmcryptdev import DMCryptDev
-from .mapperdev import MapperDev
-from ..exceptions import SysCallError, DiskError
-from ..general import SysCommand
-from ..output import log
-from ..storage import storage
-
-if TYPE_CHECKING:
- from .partition import Partition
-
-
-ROOT_DIR_PATTERN = re.compile('^.*?/devices')
-GIGA = 2 ** 30
-
-def convert_size_to_gb(size :Union[int, float]) -> float:
- return round(size / GIGA,1)
-
-def sort_block_devices_based_on_performance(block_devices :List[BlockDevice]) -> Dict[BlockDevice, int]:
- result = {device: 0 for device in block_devices}
-
- for device, weight in result.items():
- if device.spinning:
- weight -= 10
- else:
- weight += 5
-
- if device.bus_type == 'nvme':
- weight += 20
- elif device.bus_type == 'sata':
- weight += 10
-
- result[device] = weight
-
- return result
-
-def filter_disks_below_size_in_gb(devices :List[BlockDevice], gigabytes :int) -> Iterator[BlockDevice]:
- for disk in devices:
- if disk.size >= gigabytes:
- yield disk
-
-def select_largest_device(devices :List[BlockDevice], gigabytes :int, filter_out :Optional[List[BlockDevice]] = None) -> BlockDevice:
- if not filter_out:
- filter_out = []
-
- copy_devices = [*devices]
- for filter_device in filter_out:
- if filter_device in copy_devices:
- copy_devices.pop(copy_devices.index(filter_device))
-
- copy_devices = list(filter_disks_below_size_in_gb(copy_devices, gigabytes))
-
- if not len(copy_devices):
- return None
-
- return max(copy_devices, key=(lambda device : device.size))
-
-def select_disk_larger_than_or_close_to(devices :List[BlockDevice], gigabytes :int, filter_out :Optional[List[BlockDevice]] = None) -> BlockDevice:
- if not filter_out:
- filter_out = []
-
- copy_devices = [*devices]
- for filter_device in filter_out:
- if filter_device in copy_devices:
- copy_devices.pop(copy_devices.index(filter_device))
-
- if not len(copy_devices):
- return None
-
- return min(copy_devices, key=(lambda device : abs(device.size - gigabytes)))
-
-def convert_to_gigabytes(string :str) -> float:
- unit = string.strip()[-1]
- size = float(string.strip()[:-1])
-
- if unit == 'M':
- size = size / 1024
- elif unit == 'T':
- size = size * 1024
-
- return size
-
-def device_state(name :str, *args :str, **kwargs :str) -> Optional[bool]:
- # Based out of: https://askubuntu.com/questions/528690/how-to-get-list-of-all-non-removable-disk-device-names-ssd-hdd-and-sata-ide-onl/528709#528709
- if os.path.isfile('/sys/block/{}/device/block/{}/removable'.format(name, name)):
- with open('/sys/block/{}/device/block/{}/removable'.format(name, name)) as f:
- if f.read(1) == '1':
- return
-
- path = ROOT_DIR_PATTERN.sub('', os.readlink('/sys/block/{}'.format(name)))
- hotplug_buses = ("usb", "ieee1394", "mmc", "pcmcia", "firewire")
- for bus in hotplug_buses:
- if os.path.exists('/sys/bus/{}'.format(bus)):
- for device_bus in os.listdir('/sys/bus/{}/devices'.format(bus)):
- device_link = ROOT_DIR_PATTERN.sub('', os.readlink('/sys/bus/{}/devices/{}'.format(bus, device_bus)))
- if re.search(device_link, path):
- return
- return True
-
-
-def cleanup_bash_escapes(data :str) -> str:
- return data.replace(r'\ ', ' ')
-
-def blkid(cmd :str) -> Dict[str, Any]:
- if '-o' in cmd and '-o export' not in cmd:
- raise ValueError(f"blkid() requires '-o export' to be used and can therefore not continue reliably.")
- elif '-o' not in cmd:
- cmd += ' -o export'
-
- try:
- raw_data = SysCommand(cmd).decode()
- except SysCallError as error:
- log(f"Could not get block device information using blkid() using command {cmd}", level=logging.DEBUG)
- raise error
-
- result = {}
- # Process the raw result
- devname = None
- for line in raw_data.split('\r\n'):
- if not len(line):
- devname = None
- continue
-
- key, val = line.split('=', 1)
- if key.lower() == 'devname':
- devname = val
- # Lowercase for backwards compatibility with all_disks() previous use cases
- result[devname] = {
- "path": devname,
- "PATH": devname
- }
- continue
-
- result[devname][key] = cleanup_bash_escapes(val)
-
- return result
-
-def get_loop_info(path :str) -> Dict[str, Any]:
- for drive in json.loads(SysCommand(['losetup', '--json']).decode('UTF_8'))['loopdevices']:
- if not drive['name'] == path:
- continue
-
- return {
- path: {
- **drive,
- 'type' : 'loop',
- 'TYPE' : 'loop',
- 'DEVTYPE' : 'loop',
- 'PATH' : drive['name'],
- 'path' : drive['name']
- }
- }
-
- return {}
-
-def enrich_blockdevice_information(information :Dict[str, Any]) -> Dict[str, Any]:
- result = {}
- for device_path, device_information in information.items():
- dev_name = pathlib.Path(device_information['PATH']).name
- if not device_information.get('TYPE') or not device_information.get('DEVTYPE'):
- with open(f"/sys/class/block/{dev_name}/uevent") as fh:
- device_information.update(uevent(fh.read()))
-
- if (dmcrypt_name := pathlib.Path(f"/sys/class/block/{dev_name}/dm/name")).exists():
- with dmcrypt_name.open('r') as fh:
- device_information['DMCRYPT_NAME'] = fh.read().strip()
-
- result[device_path] = device_information
-
- return result
-
-def uevent(data :str) -> Dict[str, Any]:
- information = {}
-
- for line in data.replace('\r\n', '\n').split('\n'):
- if len((line := line.strip())):
- key, val = line.split('=', 1)
- information[key] = val
-
- return information
-
-def get_blockdevice_uevent(dev_name :str) -> Dict[str, Any]:
- device_information = {}
- with open(f"/sys/class/block/{dev_name}/uevent") as fh:
- device_information.update(uevent(fh.read()))
-
- return {
- f"/dev/{dev_name}" : {
- **device_information,
- 'path' : f'/dev/{dev_name}',
- 'PATH' : f'/dev/{dev_name}',
- 'PTTYPE' : None
- }
- }
-
-
-def all_disks() -> List[BlockDevice]:
- log(f"[Deprecated] archinstall.all_disks() is deprecated. Use archinstall.all_blockdevices() with the appropriate filters instead.", level=logging.WARNING, fg="yellow")
- return all_blockdevices(partitions=False, mappers=False)
-
-def get_blockdevice_info(device_path, exclude_iso_dev :bool = True) -> Dict[str, Any]:
- for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS']):
- partprobe(device_path)
- time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * retry_attempt))
-
- try:
- if exclude_iso_dev:
- # exclude all devices associated with the iso boot locations
- iso_devs = ['/run/archiso/airootfs', '/run/archiso/bootmnt']
-
- try:
- lsblk_info = get_lsblk_info(device_path)
- except DiskError:
- continue
-
- if any([dev in lsblk_info.mountpoints for dev in iso_devs]):
- continue
-
- information = blkid(f'blkid -p -o export {device_path}')
- return enrich_blockdevice_information(information)
- except SysCallError as ex:
- if ex.exit_code == 2:
- # Assume that it's a loop device, and try to get info on it
- try:
- resolved_device_name = device_path.readlink().name
- except OSError:
- resolved_device_name = device_path.name
-
- try:
- information = get_loop_info(device_path)
- if not information:
- raise SysCallError(f"Could not get loop information for {resolved_device_name}", exit_code=1)
- return enrich_blockdevice_information(information)
-
- except SysCallError:
- information = get_blockdevice_uevent(resolved_device_name)
- return enrich_blockdevice_information(information)
- else:
- # We could not reliably get any information, perhaps the disk is clean of information?
- if retry_attempt == storage['DISK_RETRY_ATTEMPTS'] - 1:
- raise ex
-
-def all_blockdevices(
- mappers: bool = False,
- partitions: bool = False,
- error: bool = False,
- exclude_iso_dev: bool = True
-) -> Dict[str, Any]:
- """
- Returns BlockDevice() and Partition() objects for all available devices.
- """
- from .partition import Partition
-
- instances = {}
-
- # Due to lsblk being highly unreliable for this use case,
- # we'll iterate the /sys/class definitions and find the information
- # from there.
- for block_device in glob.glob("/sys/class/block/*"):
- try:
- device_path = pathlib.Path(f"/dev/{pathlib.Path(block_device).readlink().name}")
- except FileNotFoundError:
- log(f"Unknown device found by '/sys/class/block/*', ignoring: {device_path}", level=logging.WARNING, fg="yellow")
-
- if device_path.exists() is False:
- log(f"Unknown device found by '/sys/class/block/*', ignoring: {device_path}", level=logging.WARNING, fg="yellow")
- continue
-
- information = get_blockdevice_info(device_path)
- if not information:
- continue
-
- for path, path_info in information.items():
- if path_info.get('DMCRYPT_NAME'):
- instances[path] = DMCryptDev(dev_path=path)
- elif path_info.get('PARTUUID') or path_info.get('PART_ENTRY_NUMBER'):
- if partitions:
- instances[path] = Partition(path, block_device=BlockDevice(get_parent_of_partition(pathlib.Path(path))))
- elif path_info.get('PTTYPE', False) is not False or path_info.get('TYPE') == 'loop':
- instances[path] = BlockDevice(path, path_info)
- elif path_info.get('TYPE') in ('squashfs', 'erofs'):
- # We can ignore squashfs devices (usually /dev/loop0 on Arch ISO)
- continue
- else:
- log(f"Unknown device found by all_blockdevices(), ignoring: {information}", level=logging.WARNING, fg="yellow")
-
- if mappers:
- for block_device in glob.glob("/dev/mapper/*"):
- if (pathobj := pathlib.Path(block_device)).is_symlink():
- instances[f"/dev/mapper/{pathobj.name}"] = MapperDev(mappername=pathobj.name)
-
- return instances
-
-
-def get_parent_of_partition(path :pathlib.Path) -> pathlib.Path:
- partition_name = path.name
- pci_device = (pathlib.Path("/sys/class/block") / partition_name).resolve()
- return f"/dev/{pci_device.parent.name}"
-
-def harddrive(size :Optional[float] = None, model :Optional[str] = None, fuzzy :bool = False) -> Optional[BlockDevice]:
- collection = all_blockdevices(partitions=False)
- for drive in collection:
- if size and convert_to_gigabytes(collection[drive]['size']) != size:
- continue
- if model and (collection[drive]['model'] is None or collection[drive]['model'].lower() != model.lower()):
- continue
-
- return collection[drive]
-
-def split_bind_name(path :Union[pathlib.Path, str]) -> list:
- # log(f"[Deprecated] Partition().subvolumes now contain the split bind name via it's subvolume.name instead.", level=logging.WARNING, fg="yellow")
- # we check for the bind notation. if exist we'll only use the "true" device path
- if '[' in str(path) : # is a bind path (btrfs subvolume path)
- device_path, bind_path = str(path).split('[')
- bind_path = bind_path[:-1].strip() # remove the ]
- else:
- device_path = path
- bind_path = None
- return device_path,bind_path
-
-def find_mountpoint(device_path :str) -> Dict[str, Any]:
- try:
- for filesystem in json.loads(SysCommand(f'/usr/bin/findmnt -R --json {device_path}').decode())['filesystems']:
- yield filesystem
- except SysCallError:
- return {}
-
-def findmnt(path :pathlib.Path, traverse :bool = False, ignore :List = [], recurse :bool = True) -> Dict[str, Any]:
- for traversal in list(map(str, [str(path)] + list(path.parents))):
- if traversal in ignore:
- continue
-
- try:
- log(f"Getting mount information for device path {traversal}", level=logging.DEBUG)
- if (output := SysCommand(f"/usr/bin/findmnt --json {'--submounts' if recurse else ''} {traversal}").decode('UTF-8')):
- return json.loads(output)
-
- except SysCallError as error:
- log(f"Could not get mount information on {path} but continuing and ignoring: {error}", level=logging.INFO, fg="gray")
- pass
-
- if not traverse:
- break
-
- raise DiskError(f"Could not get mount information for path {path}")
-
-
-def get_mount_info(path :Union[pathlib.Path, str], traverse :bool = False, return_real_path :bool = False, ignore :List = []) -> Dict[str, Any]:
- import traceback
-
- log(f"Deprecated: archinstall.get_mount_info(). Use archinstall.findmnt() instead, which does not do any automatic parsing. Please change at:\n{''.join(traceback.format_stack())}")
- device_path, bind_path = split_bind_name(path)
- output = {}
-
- for traversal in list(map(str, [str(device_path)] + list(pathlib.Path(str(device_path)).parents))):
- if traversal in ignore:
- continue
-
- try:
- log(f"Getting mount information for device path {traversal}", level=logging.DEBUG)
- if (output := SysCommand(f'/usr/bin/findmnt --json {traversal}').decode('UTF-8')):
- break
-
- except SysCallError as error:
- print('ERROR:', error)
- pass
-
- if not traverse:
- break
-
- if not output:
- raise DiskError(f"Could not get mount information for device path {device_path}")
-
- output = json.loads(output)
-
- # for btrfs partitions we redice the filesystem list to the one with the source equals to the parameter
- # i.e. the subvolume filesystem we're searching for
- if 'filesystems' in output and len(output['filesystems']) > 1 and bind_path is not None:
- output['filesystems'] = [entry for entry in output['filesystems'] if entry['source'] == str(path)]
-
- if 'filesystems' in output:
- if len(output['filesystems']) > 1:
- raise DiskError(f"Path '{device_path}' contains multiple mountpoints: {output['filesystems']}")
-
- if return_real_path:
- return output['filesystems'][0], traversal
- else:
- return output['filesystems'][0]
-
- if return_real_path:
- return {}, traversal
- else:
- return {}
-
-
-def get_all_targets(data :Dict[str, Any], filters :Dict[str, None] = {}) -> Dict[str, None]:
- for info in data:
- if info.get('target') not in filters:
- filters[info.get('target')] = None
-
- filters.update(get_all_targets(info.get('children', [])))
-
- return filters
-
-def get_partitions_in_use(mountpoint :str) -> Dict[str, Any]:
- from .partition import Partition
-
- try:
- output = SysCommand(f"/usr/bin/findmnt --json -R {mountpoint}").decode('UTF-8')
- except SysCallError:
- return {}
-
- if not output:
- return {}
-
- output = json.loads(output)
-
- mounts = {}
-
- block_devices_available = all_blockdevices(mappers=True, partitions=True, error=True)
-
- block_devices_mountpoints = {}
- for blockdev in block_devices_available.values():
- if not type(blockdev) in (Partition, MapperDev):
- continue
-
- if isinstance(blockdev, Partition):
- if blockdev.mountpoints:
- for blockdev_mountpoint in blockdev.mountpoints:
- block_devices_mountpoints[blockdev_mountpoint] = blockdev
- else:
- if blockdev.mount_information:
- for blockdev_mountpoint in blockdev.mount_information:
- block_devices_mountpoints[blockdev_mountpoint['target']] = blockdev
-
- log(f'Filtering available mounts {block_devices_mountpoints} to those under {mountpoint}', level=logging.DEBUG)
-
- for mountpoint in list(get_all_targets(output['filesystems']).keys()):
- # Since all_blockdevices() returns PosixPath objects, we need to convert
- # findmnt paths to pathlib.Path() first:
- mountpoint = pathlib.Path(mountpoint)
-
- if mountpoint in block_devices_mountpoints:
- if mountpoint not in mounts:
- mounts[mountpoint] = block_devices_mountpoints[mountpoint]
- # If the already defined mountpoint is a DMCryptDev, and the newly found
- # mountpoint is a MapperDev, it has precedence and replaces the old mountpoint definition.
- elif type(mounts[mountpoint]) == DMCryptDev and type(block_devices_mountpoints[mountpoint]) == MapperDev:
- mounts[mountpoint] = block_devices_mountpoints[mountpoint]
-
- log(f"Available partitions: {mounts}", level=logging.DEBUG)
-
- return mounts
-
-
-def get_filesystem_type(path :str) -> Optional[str]:
- try:
- return SysCommand(f"blkid -o value -s TYPE {path}").decode('UTF-8').strip()
- except SysCallError:
- return None
-
-
-def disk_layouts() -> Optional[Dict[str, Any]]:
- try:
- if (handle := SysCommand("lsblk -f -o+TYPE,SIZE -J")).exit_code == 0:
- return {str(key): val for key, val in json.loads(handle.decode('UTF-8')).items()}
- else:
- log(f"Could not return disk layouts: {handle}", level=logging.WARNING, fg="yellow")
- return None
- except SysCallError as err:
- log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow")
- return None
- except json.decoder.JSONDecodeError as err:
- log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow")
- return None
-
-
-def find_partition_by_mountpoint(block_devices :List[BlockDevice], relative_mountpoint :str) -> Partition:
- for device in block_devices:
- for partition in block_devices[device]['partitions']:
- if partition.get('mountpoint', None) == relative_mountpoint:
- return partition
-
-def partprobe(path :str = '') -> bool:
- try:
- if SysCommand(f'bash -c "partprobe {path}"').exit_code == 0:
- return True
- except SysCallError:
- pass
- return False
-
-def convert_device_to_uuid(path :str) -> str:
- device_name, bind_name = split_bind_name(path)
-
- for i in range(storage['DISK_RETRY_ATTEMPTS']):
- partprobe(device_name)
- time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i)) # TODO: Remove, we should be relying on blkid instead of lsblk
-
- # TODO: Convert lsblk to blkid
- # (lsblk supports BlockDev and Partition UUID grabbing, blkid requires you to pick PTUUID and PARTUUID)
- output = json.loads(SysCommand(f"lsblk --json -o+UUID {device_name}").decode('UTF-8'))
-
- for device in output['blockdevices']:
- if (dev_uuid := device.get('uuid', None)):
- return dev_uuid
-
- raise DiskError(f"Could not retrieve the UUID of {path} within a timely manner.")
-
-
-def has_mountpoint(partition: Union[dict,Partition,MapperDev], target: str, strict: bool = True) -> bool:
- """ Determine if a certain partition is mounted (or has a mountpoint) as specific target (path)
- Coded for clarity rather than performance
-
- Input parms:
- :parm partition the partition we check
- :type Either a Partition object or a dict with the contents of a partition definition in the disk_layouts schema
-
- :parm target (a string representing a mount path we want to check for.
- :type str
-
- :parm strict if the check will be strict, target is exactly the mountpoint, or no, where the target is a leaf (f.i. to check if it is in /mnt/archinstall/). Not available for root check ('/') for obvious reasons
-
- """
- # we create the mountpoint list
- if isinstance(partition,dict):
- subvolumes: List[Subvolume] = partition.get('btrfs',{}).get('subvolumes', [])
- mountpoints = [partition.get('mountpoint')]
- mountpoints += [volume.mountpoint for volume in subvolumes]
- else:
- mountpoints = [partition.mountpoint,] + [subvol.target for subvol in partition.subvolumes]
-
- # we check
- if strict or target == '/':
- if target in mountpoints:
- return True
- else:
- return False
- else:
- for mp in mountpoints:
- if mp and mp.endswith(target):
- return True
- return False
diff --git a/archinstall/lib/disk/mapperdev.py b/archinstall/lib/disk/mapperdev.py
deleted file mode 100644
index bf1b3583..00000000
--- a/archinstall/lib/disk/mapperdev.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import glob
-import pathlib
-import logging
-import json
-from dataclasses import dataclass
-from typing import Optional, List, Dict, Any, Iterator, TYPE_CHECKING
-
-from ..exceptions import SysCallError
-from ..general import SysCommand
-from ..output import log
-
-if TYPE_CHECKING:
- from .btrfs import BtrfsSubvolumeInfo
-
-@dataclass
-class MapperDev:
- mappername :str
-
- @property
- def name(self):
- return self.mappername
-
- @property
- def path(self):
- return f"/dev/mapper/{self.mappername}"
-
- @property
- def part_uuid(self):
- return self.partition.part_uuid
-
- @property
- def partition(self):
- from .helpers import uevent, get_parent_of_partition
- from .partition import Partition
- from .blockdevice import BlockDevice
-
- for mapper in glob.glob('/dev/mapper/*'):
- path_obj = pathlib.Path(mapper)
- if path_obj.name == self.mappername and pathlib.Path(mapper).is_symlink():
- dm_device = (pathlib.Path("/dev/mapper/") / path_obj.readlink()).resolve()
-
- for slave in glob.glob(f"/sys/class/block/{dm_device.name}/slaves/*"):
- partition_belonging_to_dmcrypt_device = pathlib.Path(slave).name
-
- try:
- uevent_data = SysCommand(f"blkid -o export /dev/{partition_belonging_to_dmcrypt_device}").decode()
- except SysCallError as error:
- log(f"Could not get information on device /dev/{partition_belonging_to_dmcrypt_device}: {error}", level=logging.ERROR, fg="red")
-
- information = uevent(uevent_data)
- block_device = BlockDevice(get_parent_of_partition('/dev/' / pathlib.Path(information['DEVNAME'])))
-
- return Partition(information['DEVNAME'], block_device=block_device)
-
- raise ValueError(f"Could not convert {self.mappername} to a real dm-crypt device")
-
- @property
- def mountpoint(self) -> Optional[pathlib.Path]:
- try:
- data = json.loads(SysCommand(f"findmnt --json -R {self.path}").decode())
- for filesystem in data['filesystems']:
- return pathlib.Path(filesystem.get('target'))
-
- except SysCallError as error:
- # Not mounted anywhere most likely
- log(f"Could not locate mount information for {self.path}: {error}", level=logging.WARNING, fg="yellow")
- pass
-
- return None
-
- @property
- def mountpoints(self) -> List[Dict[str, Any]]:
- return [obj['target'] for obj in self.mount_information]
-
- @property
- def mount_information(self) -> List[Dict[str, Any]]:
- from .helpers import find_mountpoint
- return [{**obj, 'target' : pathlib.Path(obj.get('target', '/dev/null'))} for obj in find_mountpoint(self.path)]
-
- @property
- def filesystem(self) -> Optional[str]:
- from .helpers import get_filesystem_type
- return get_filesystem_type(self.path)
-
- @property
- def subvolumes(self) -> Iterator['BtrfsSubvolumeInfo']:
- from .btrfs import subvolume_info_from_path
-
- for mountpoint in self.mount_information:
- if target := mountpoint.get('target'):
- if subvolume := subvolume_info_from_path(pathlib.Path(target)):
- yield subvolume
diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py
deleted file mode 100644
index 87eaa6a7..00000000
--- a/archinstall/lib/disk/partition.py
+++ /dev/null
@@ -1,661 +0,0 @@
-import glob
-import time
-import logging
-import json
-import os
-import hashlib
-import typing
-from dataclasses import dataclass, field
-from pathlib import Path
-from typing import Optional, Dict, Any, List, Union, Iterator
-
-from .blockdevice import BlockDevice
-from .helpers import get_filesystem_type, convert_size_to_gb, split_bind_name
-from ..storage import storage
-from ..exceptions import DiskError, SysCallError, UnknownFilesystemFormat
-from ..output import log
-from ..general import SysCommand
-from .btrfs.btrfs_helpers import subvolume_info_from_path
-from .btrfs.btrfssubvolumeinfo import BtrfsSubvolumeInfo
-
-@dataclass
-class PartitionInfo:
- partition_object: 'Partition'
- device_path: str # This would be /dev/sda1 for instance
- bootable: bool
- size: float
- sector_size: int
- start: Optional[int]
- end: Optional[int]
- pttype: Optional[str]
- filesystem_type: Optional[str]
- partuuid: Optional[str]
- uuid: Optional[str]
- mountpoints: List[Path] = field(default_factory=list)
-
- def __post_init__(self):
- if not all([self.partuuid, self.uuid]):
- for i in range(storage['DISK_RETRY_ATTEMPTS']):
- lsblk_info = SysCommand(f"lsblk --json -b -o+LOG-SEC,SIZE,PTTYPE,PARTUUID,UUID,FSTYPE {self.device_path}").decode('UTF-8')
- try:
- lsblk_info = json.loads(lsblk_info)
- except json.decoder.JSONDecodeError:
- log(f"Could not decode JSON: {lsblk_info}", fg="red", level=logging.ERROR)
- raise DiskError(f'Failed to retrieve information for "{self.device_path}" with lsblk')
-
- if not (device := lsblk_info.get('blockdevices', [None])[0]):
- raise DiskError(f'Failed to retrieve information for "{self.device_path}" with lsblk')
-
- self.partuuid = device.get('partuuid')
- self.uuid = device.get('uuid')
-
- # Lets build a list of requirements that we would like
- # to retry and build (stuff that can take time between partprobes)
- requirements = []
- requirements.append(self.partuuid)
-
- # Unformatted partitions won't have a UUID
- if lsblk_info.get('fstype') is not None:
- requirements.append(self.uuid)
-
- if all(requirements):
- break
-
- self.partition_object.partprobe()
- time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i))
-
- def get_first_mountpoint(self) -> Optional[Path]:
- if len(self.mountpoints) > 0:
- return self.mountpoints[0]
- return None
-
-
-class Partition:
- def __init__(
- self,
- path: str,
- block_device: BlockDevice,
- part_id :Optional[str] = None,
- filesystem :Optional[str] = None,
- mountpoint :Optional[str] = None,
- encrypted :bool = False,
- autodetect_filesystem :bool = True,
- ):
- if not part_id:
- part_id = os.path.basename(path)
-
- if type(block_device) is str:
- raise ValueError(f"Partition()'s 'block_device' parameter has to be a archinstall.BlockDevice() instance!")
-
- self.block_device = block_device
- self._path = path
- self._part_id = part_id
- self._target_mountpoint = mountpoint
- self._encrypted = encrypted
- self._wipe = False
- self._type = 'primary'
-
- if mountpoint:
- self.mount(mountpoint)
-
- try:
- self._partition_info = self._fetch_information()
-
- if not autodetect_filesystem and filesystem:
- self._partition_info.filesystem_type = filesystem
-
- if self._partition_info.filesystem_type == 'crypto_LUKS':
- self._encrypted = True
- except DiskError:
- self._partition_info = None
-
- @typing.no_type_check # I hate doint this but I'm currently unsure where this is used.
- def __lt__(self, left_comparitor :BlockDevice) -> bool:
- if type(left_comparitor) == Partition:
- left_comparitor = left_comparitor.path
- else:
- left_comparitor = str(left_comparitor)
-
- # The goal is to check if /dev/nvme0n1p1 comes before /dev/nvme0n1p5
- return self._path < left_comparitor
-
- def __repr__(self, *args :str, **kwargs :str) -> str:
- mount_repr = ''
- if self._partition_info:
- if mountpoint := self._partition_info.get_first_mountpoint():
- mount_repr = f", mounted={mountpoint}"
- elif self._target_mountpoint:
- mount_repr = f", rel_mountpoint={self._target_mountpoint}"
-
- classname = self.__class__.__name__
-
- if not self._partition_info:
- return f'{classname}(path={self._path})'
- elif self._encrypted:
- return f'{classname}(path={self._path}, size={self.size}, PARTUUID={self.part_uuid}, parent={self.real_device}, fs={self._partition_info.filesystem_type}{mount_repr})'
- else:
- return f'{classname}(path={self._path}, size={self.size}, PARTUUID={self.part_uuid}, fs={self._partition_info.filesystem_type}{mount_repr})'
-
- def as_json(self) -> Dict[str, Any]:
- """
- this is used for the table representation of the partition (see FormattedOutput)
- """
- partition_info = {
- 'type': self._type,
- 'PARTUUID': self.part_uuid,
- 'wipe': self._wipe,
- 'boot': self.boot,
- 'ESP': self.boot,
- 'mountpoint': self._target_mountpoint,
- 'encrypted': self._encrypted,
- 'start': self.start,
- 'size': self.end,
- 'filesystem': self._partition_info.filesystem_type if self._partition_info else 'Unknown'
- }
-
- return partition_info
-
- def __dump__(self) -> Dict[str, Any]:
- # TODO remove this in favour of as_json
- return {
- 'type': self._type,
- 'PARTUUID': self.part_uuid,
- 'wipe': self._wipe,
- 'boot': self.boot,
- 'ESP': self.boot,
- 'mountpoint': self._target_mountpoint,
- 'encrypted': self._encrypted,
- 'start': self.start,
- 'size': self.end,
- 'filesystem': {
- 'format': self._partition_info.filesystem_type if self._partition_info else 'None'
- }
- }
-
- def _call_lsblk(self) -> Dict[str, Any]:
- for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS']):
- self.partprobe()
- time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * retry_attempt)) # TODO: Remove, we should be relying on blkid instead of lsblk
- # This sleep might be overkill, but lsblk is known to
- # work against a chaotic cache that can change during call
- # causing no information to be returned (blkid is better)
- # time.sleep(1)
-
- # TODO: Maybe incorporate a re-try system here based on time.sleep(max(0.1, storage.get('DISK_TIMEOUTS', 1)))
-
- try:
- output = SysCommand(f"lsblk --json -b -o+LOG-SEC,SIZE,PTTYPE,PARTUUID,UUID,FSTYPE {self.device_path}").decode('UTF-8')
- except SysCallError as error:
- # Get the output minus the message/info from lsblk if it returns a non-zero exit code.
- output = error.worker.decode('UTF-8')
- if '{' in output:
- output = output[output.find('{'):]
-
- if output:
- try:
- lsblk_info = json.loads(output)
- return lsblk_info
- except json.decoder.JSONDecodeError:
- log(f"Could not decode JSON: {output}", fg="red", level=logging.ERROR)
-
- raise DiskError(f'Failed to get partition information "{self.device_path}" with lsblk')
-
- def _call_sfdisk(self) -> Dict[str, Any]:
- output = SysCommand(f"sfdisk --json {self.block_device.path}").decode('UTF-8')
-
- if output:
- sfdisk_info = json.loads(output)
- partitions = sfdisk_info.get('partitiontable', {}).get('partitions', [])
- node = list(filter(lambda x: x['node'] == self._path, partitions))
-
- if len(node) > 0:
- return node[0]
-
- return {}
-
- raise DiskError(f'Failed to read disk "{self.block_device.path}" with sfdisk')
-
- def _fetch_information(self) -> PartitionInfo:
- lsblk_info = self._call_lsblk()
- sfdisk_info = self._call_sfdisk()
-
- if not (device := lsblk_info.get('blockdevices', [])):
- raise DiskError(f'Failed to retrieve information for "{self.device_path}" with lsblk')
-
- # Grab the first (and only) block device in the list as we're targeting a specific partition
- device = device[0]
-
- mountpoints = [Path(mountpoint) for mountpoint in device['mountpoints'] if mountpoint]
- bootable = sfdisk_info.get('bootable', False) or sfdisk_info.get('type', '') == 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B'
-
- return PartitionInfo(
- partition_object=self,
- device_path=self._path,
- pttype=device['pttype'],
- partuuid=device['partuuid'],
- uuid=device['uuid'],
- sector_size=device['log-sec'],
- size=convert_size_to_gb(device['size']),
- start=sfdisk_info.get('start', None),
- end=sfdisk_info.get('size', None),
- bootable=bootable,
- filesystem_type=device['fstype'],
- mountpoints=mountpoints
- )
-
- @property
- def target_mountpoint(self) -> Optional[str]:
- return self._target_mountpoint
-
- @property
- def path(self) -> str:
- return self._path
-
- @property
- def filesystem(self) -> str:
- if self._partition_info:
- return self._partition_info.filesystem_type
-
- @property
- def mountpoint(self) -> Optional[Path]:
- if len(self.mountpoints) > 0:
- return self.mountpoints[0]
- return None
-
- @property
- def mountpoints(self) -> List[Path]:
- if self._partition_info:
- return self._partition_info.mountpoints
-
- @property
- def sector_size(self) -> int:
- if self._partition_info:
- return self._partition_info.sector_size
-
- @property
- def start(self) -> Optional[int]:
- if self._partition_info:
- return self._partition_info.start
-
- @property
- def end(self) -> Optional[int]:
- if self._partition_info:
- return self._partition_info.end
-
- @property
- def end_sectors(self) -> Optional[int]:
- if self._partition_info:
- start = self._partition_info.start
- end = self._partition_info.end
- if start and end:
- return start + end
-
- @property
- def size(self) -> Optional[float]:
- if self._partition_info:
- return self._partition_info.size
-
- @property
- def boot(self) -> bool:
- if self._partition_info:
- return self._partition_info.bootable
-
- @property
- def partition_type(self) -> Optional[str]:
- if self._partition_info:
- return self._partition_info.pttype
-
- @property
- def part_uuid(self) -> str:
- if self._partition_info:
- return self._partition_info.partuuid
-
- @property
- def uuid(self) -> Optional[str]:
- """
- Returns the UUID as returned by lsblk for the **partition**.
- This is more reliable than relying on /dev/disk/by-uuid as
- it doesn't seam to be able to detect md raid partitions.
- For bind mounts all the subvolumes share the same uuid
- """
- for i in range(storage['DISK_RETRY_ATTEMPTS']):
- if not self.partprobe():
- raise DiskError(f"Could not perform partprobe on {self.device_path}")
-
- time.sleep(storage.get('DISK_TIMEOUTS', 1) * i)
-
- partuuid = self._safe_uuid
- if partuuid:
- return partuuid
-
- raise DiskError(f"Could not get PARTUUID for {self.path} using 'blkid -s PARTUUID -o value {self.path}'")
-
- @property
- def _safe_uuid(self) -> Optional[str]:
- """
- A near copy of self.uuid but without any delays.
- This function should only be used where uuid is not crucial.
- For instance when you want to get a __repr__ of the class.
- """
- if not self.partprobe():
- if self.block_device.partition_type == 'iso9660':
- return None
-
- log(f"Could not reliably refresh PARTUUID of partition {self.device_path} due to partprobe error.", level=logging.DEBUG)
-
- try:
- return SysCommand(f'blkid -s UUID -o value {self.device_path}').decode('UTF-8').strip()
- except SysCallError as error:
- if self.block_device.partition_type == 'iso9660':
- # Parent device is a Optical Disk (.iso dd'ed onto a device for instance)
- return None
-
- log(f"Could not get PARTUUID of partition using 'blkid -s UUID -o value {self.device_path}': {error}")
-
- @property
- def _safe_part_uuid(self) -> Optional[str]:
- """
- A near copy of self.uuid but without any delays.
- This function should only be used where uuid is not crucial.
- For instance when you want to get a __repr__ of the class.
- """
- if not self.partprobe():
- if self.block_device.partition_type == 'iso9660':
- return None
-
- log(f"Could not reliably refresh PARTUUID of partition {self.device_path} due to partprobe error.", level=logging.DEBUG)
-
- try:
- return SysCommand(f'blkid -s PARTUUID -o value {self.device_path}').decode('UTF-8').strip()
- except SysCallError as error:
- if self.block_device.partition_type == 'iso9660':
- # Parent device is a Optical Disk (.iso dd'ed onto a device for instance)
- return None
-
- log(f"Could not get PARTUUID of partition using 'blkid -s PARTUUID -o value {self.device_path}': {error}")
-
- if self._partition_info:
- return self._partition_info.uuid
-
- @property
- def encrypted(self) -> Union[bool, None]:
- return self._encrypted
-
- @property
- def parent(self) -> str:
- return self.real_device
-
- @property
- def real_device(self) -> str:
- output = SysCommand('lsblk -J').decode('UTF-8')
-
- if output:
- for blockdevice in json.loads(output)['blockdevices']:
- if parent := self.find_parent_of(blockdevice, os.path.basename(self.device_path)):
- return f"/dev/{parent}"
- return self._path
-
- raise DiskError('Unable to get disk information for command "lsblk -J"')
-
- @property
- def device_path(self) -> str:
- """ for bind mounts returns the physical path of the partition
- """
- device_path, bind_name = split_bind_name(self._path)
- return device_path
-
- @property
- def bind_name(self) -> str:
- """ for bind mounts returns the bind name (subvolume path).
- Returns none if this property does not exist
- """
- device_path, bind_name = split_bind_name(self._path)
- return bind_name
-
- @property
- def subvolumes(self) -> Iterator[BtrfsSubvolumeInfo]:
- from .helpers import findmnt
-
- def iterate_children_recursively(information):
- for child in information.get('children', []):
- if target := child.get('target'):
- if child.get('fstype') == 'btrfs':
- if subvolume := subvolume_info_from_path(Path(target)):
- yield subvolume
-
- if child.get('children'):
- for subchild in iterate_children_recursively(child):
- yield subchild
-
- if self._partition_info.filesystem_type == 'btrfs':
- for mountpoint in self._partition_info.mountpoints:
- if result := findmnt(mountpoint):
- for filesystem in result.get('filesystems', []):
- if subvolume := subvolume_info_from_path(mountpoint):
- yield subvolume
-
- for child in iterate_children_recursively(filesystem):
- yield child
-
- def partprobe(self) -> bool:
- try:
- if self.block_device:
- return 0 == SysCommand(f'partprobe {self.block_device.device}').exit_code
- except SysCallError as error:
- log(f"Unreliable results might be given for {self._path} due to partprobe error: {error}", level=logging.DEBUG)
-
- return False
-
- def detect_inner_filesystem(self, password :str) -> Optional[str]:
- log(f'Trying to detect inner filesystem format on {self} (This might take a while)', level=logging.INFO)
- from ..luks import luks2
-
- try:
- with luks2(self, storage.get('ENC_IDENTIFIER', 'ai') + 'loop', password, auto_unmount=True) as unlocked_device:
- return unlocked_device.filesystem
- except SysCallError:
- pass
- return None
-
- def has_content(self) -> bool:
- fs_type = self._partition_info.filesystem_type
- if not fs_type or "swap" in fs_type:
- return False
-
- temporary_mountpoint = '/tmp/' + hashlib.md5(bytes(f"{time.time()}", 'UTF-8') + os.urandom(12)).hexdigest()
- temporary_path = Path(temporary_mountpoint)
-
- temporary_path.mkdir(parents=True, exist_ok=True)
- if (handle := SysCommand(f'/usr/bin/mount {self._path} {temporary_mountpoint}')).exit_code != 0:
- raise DiskError(f'Could not mount and check for content on {self._path} because: {handle}')
-
- files = len(glob.glob(f"{temporary_mountpoint}/*"))
- iterations = 0
- while SysCommand(f"/usr/bin/umount -R {temporary_mountpoint}").exit_code != 0 and (iterations := iterations + 1) < 10:
- time.sleep(1)
-
- temporary_path.rmdir()
-
- return True if files > 0 else False
-
- def encrypt(self, password: Optional[str] = None) -> str:
- """
- A wrapper function for luks2() instances and the .encrypt() method of that instance.
- """
- from ..luks import luks2
-
- handle = luks2(self, None, None)
- return handle.encrypt(self, password=password)
-
- def format(self, filesystem :Optional[str] = None, path :Optional[str] = None, log_formatting :bool = True, options :List[str] = [], retry :bool = True) -> bool:
- """
- Format can be given an overriding path, for instance /dev/null to test
- the formatting functionality and in essence the support for the given filesystem.
- """
- if filesystem is None:
- filesystem = self._partition_info.filesystem_type
-
- if path is None:
- path = self._path
-
- # This converts from fat32 -> vfat to unify filesystem names
- filesystem = get_mount_fs_type(filesystem)
-
- # To avoid "unable to open /dev/x: No such file or directory"
- start_wait = time.time()
- while Path(path).exists() is False and time.time() - start_wait < 10:
- time.sleep(0.025)
-
- if log_formatting:
- log(f'Formatting {path} -> {filesystem}', level=logging.INFO)
-
- try:
- if filesystem == 'btrfs':
- options = ['-f'] + options
-
- mkfs = SysCommand(f"/usr/bin/mkfs.btrfs {' '.join(options)} {path}").decode('UTF-8')
- if mkfs and 'UUID:' not in mkfs:
- raise DiskError(f'Could not format {path} with {filesystem} because: {mkfs}')
- self._partition_info.filesystem_type = filesystem
-
- elif filesystem == 'vfat':
- options = ['-F32'] + options
- log(f"/usr/bin/mkfs.vfat {' '.join(options)} {path}")
- if (handle := SysCommand(f"/usr/bin/mkfs.vfat {' '.join(options)} {path}")).exit_code != 0:
- raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
- self._partition_info.filesystem_type = filesystem
-
- elif filesystem == 'ext4':
- options = ['-F'] + options
-
- if (handle := SysCommand(f"/usr/bin/mkfs.ext4 {' '.join(options)} {path}")).exit_code != 0:
- raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
- self._partition_info.filesystem_type = filesystem
-
- elif filesystem == 'ext2':
- options = ['-F'] + options
-
- if (handle := SysCommand(f"/usr/bin/mkfs.ext2 {' '.join(options)} {path}")).exit_code != 0:
- raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
- self._partition_info.filesystem_type = 'ext2'
- elif filesystem == 'xfs':
- options = ['-f'] + options
-
- if (handle := SysCommand(f"/usr/bin/mkfs.xfs {' '.join(options)} {path}")).exit_code != 0:
- raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
- self._partition_info.filesystem_type = filesystem
-
- elif filesystem == 'f2fs':
- options = ['-f'] + options
-
- if (handle := SysCommand(f"/usr/bin/mkfs.f2fs {' '.join(options)} {path}")).exit_code != 0:
- raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
- self._partition_info.filesystem_type = filesystem
-
- elif filesystem == 'ntfs3':
- options = ['-f'] + options
-
- if (handle := SysCommand(f"/usr/bin/mkfs.ntfs -Q {' '.join(options)} {path}")).exit_code != 0:
- raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
- self._partition_info.filesystem_type = filesystem
-
- elif filesystem == 'crypto_LUKS':
- # from ..luks import luks2
- # encrypted_partition = luks2(self, None, None)
- # encrypted_partition.format(path)
- self._partition_info.filesystem_type = filesystem
-
- else:
- raise UnknownFilesystemFormat(f"Fileformat '{filesystem}' is not yet implemented.")
- except SysCallError as error:
- log(f"Formatting ran in to an error: {error}", level=logging.WARNING, fg="orange")
- if retry is True:
- log(f"Retrying in {storage.get('DISK_TIMEOUTS', 1)} seconds.", level=logging.WARNING, fg="orange")
- time.sleep(storage.get('DISK_TIMEOUTS', 1))
-
- return self.format(filesystem, path, log_formatting, options, retry=False)
-
- if get_filesystem_type(path) == 'crypto_LUKS' or get_filesystem_type(self.real_device) == 'crypto_LUKS':
- self._encrypted = True
- else:
- self._encrypted = False
-
- return True
-
- def find_parent_of(self, data :Dict[str, Any], name :str, parent :Optional[str] = None) -> Optional[str]:
- if data['name'] == name:
- return parent
- elif 'children' in data:
- for child in data['children']:
- if parent := self.find_parent_of(child, name, parent=data['name']):
- return parent
-
- return None
-
- def mount(self, target :str, fs :Optional[str] = None, options :str = '') -> bool:
- if not self._partition_info.get_first_mountpoint():
- log(f'Mounting {self} to {target}', level=logging.INFO)
-
- if not fs:
- fs = self._partition_info.filesystem_type
-
- fs_type = get_mount_fs_type(fs)
-
- Path(target).mkdir(parents=True, exist_ok=True)
-
- if self.bind_name:
- device_path = self.device_path
- # TODO options should be better be a list than a string
- if options:
- options = f"{options},subvol={self.bind_name}"
- else:
- options = f"subvol={self.bind_name}"
- else:
- device_path = self._path
- try:
- if options:
- mnt_handle = SysCommand(f"/usr/bin/mount -t {fs_type} -o {options} {device_path} {target}")
- else:
- mnt_handle = SysCommand(f"/usr/bin/mount -t {fs_type} {device_path} {target}")
-
- # TODO: Should be redundant to check for exit_code
- if mnt_handle.exit_code != 0:
- raise DiskError(f"Could not mount {self._path} to {target} using options {options}")
- except SysCallError as err:
- raise err
-
- # Update the partition info since the mount info has changed after this call.
- self._partition_info = self._fetch_information()
- return True
-
- return False
-
- def unmount(self) -> bool:
- SysCommand(f"/usr/bin/umount {self._path}")
-
- # Update the partition info since the mount info has changed after this call.
- self._partition_info = self._fetch_information()
- return True
-
- def filesystem_supported(self) -> bool:
- """
- The support for a filesystem (this partition) is tested by calling
- partition.format() with a path set to '/dev/null' which returns two exceptions:
- 1. SysCallError saying that /dev/null is not formattable - but the filesystem is supported
- 2. UnknownFilesystemFormat that indicates that we don't support the given filesystem type
- """
- try:
- self.format(self._partition_info.filesystem_type, '/dev/null', log_formatting=False)
- except (SysCallError, DiskError):
- pass # We supported it, but /dev/null is not formattable as expected so the mkfs call exited with an error code
- except UnknownFilesystemFormat as err:
- raise err
- return True
-
-
-def get_mount_fs_type(fs :str) -> str:
- if fs == 'ntfs':
- return 'ntfs3' # Needed to use the Paragon R/W NTFS driver
- elif fs == 'fat32':
- return 'vfat' # This is the actual type used for fat32 mounting
- return fs
diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py
new file mode 100644
index 00000000..fb1eb74b
--- /dev/null
+++ b/archinstall/lib/disk/partitioning_menu.py
@@ -0,0 +1,429 @@
+from __future__ import annotations
+
+import re
+from pathlib import Path
+from typing import Any, TYPE_CHECKING, List, Optional, Tuple
+from dataclasses import dataclass
+
+from .device_model import (
+ PartitionModification, FilesystemType, BDevice,
+ Size, Unit, PartitionType, PartitionFlag,
+ ModificationStatus, DeviceGeometry, SectorSize, BtrfsMountOption
+)
+from ..hardware import SysInfo
+from ..menu import Menu, ListManager, MenuSelection, TextInput
+from ..output import FormattedOutput, warn
+from .subvolume_menu import SubvolumeMenu
+
+if TYPE_CHECKING:
+ _: Any
+
+
+@dataclass
+class DefaultFreeSector:
+ start: Size
+ end: Size
+
+
+class PartitioningList(ListManager):
+ """
+ subclass of ListManager for the managing of user accounts
+ """
+ def __init__(self, prompt: str, device: BDevice, device_partitions: List[PartitionModification]):
+ self._device = device
+ self._actions = {
+ 'create_new_partition': str(_('Create a new partition')),
+ 'suggest_partition_layout': str(_('Suggest partition layout')),
+ 'remove_added_partitions': str(_('Remove all newly added partitions')),
+ 'assign_mountpoint': str(_('Assign mountpoint')),
+ 'mark_formatting': str(_('Mark/Unmark to be formatted (wipes data)')),
+ 'mark_bootable': str(_('Mark/Unmark as bootable')),
+ 'set_filesystem': str(_('Change filesystem')),
+ 'btrfs_mark_compressed': str(_('Mark/Unmark as compressed')), # btrfs only
+ 'btrfs_mark_nodatacow': str(_('Mark/Unmark as nodatacow')), # btrfs only
+ 'btrfs_set_subvolumes': str(_('Set subvolumes')), # btrfs only
+ 'delete_partition': str(_('Delete partition'))
+ }
+
+ display_actions = list(self._actions.values())
+ super().__init__(prompt, device_partitions, display_actions[:2], display_actions[3:])
+
+ def selected_action_display(self, partition: PartitionModification) -> str:
+ return str(_('Partition'))
+
+ def filter_options(self, selection: PartitionModification, options: List[str]) -> List[str]:
+ not_filter = []
+
+ # only display formatting if the partition exists already
+ if not selection.exists():
+ not_filter += [self._actions['mark_formatting']]
+ else:
+ # only allow options if the existing partition
+ # was marked as formatting, otherwise we run into issues where
+ # 1. select a new fs -> potentially mark as wipe now
+ # 2. Switch back to old filesystem -> should unmark wipe now, but
+ # how do we know it was the original one?
+ not_filter += [
+ self._actions['set_filesystem'],
+ self._actions['mark_bootable'],
+ self._actions['btrfs_mark_compressed'],
+ self._actions['btrfs_mark_nodatacow'],
+ self._actions['btrfs_set_subvolumes']
+ ]
+
+ # non btrfs partitions shouldn't get btrfs options
+ if selection.fs_type != FilesystemType.Btrfs:
+ not_filter += [
+ self._actions['btrfs_mark_compressed'],
+ self._actions['btrfs_mark_nodatacow'],
+ self._actions['btrfs_set_subvolumes']
+ ]
+ else:
+ not_filter += [self._actions['assign_mountpoint']]
+
+ return [o for o in options if o not in not_filter]
+
+ def handle_action(
+ self,
+ action: str,
+ entry: Optional[PartitionModification],
+ data: List[PartitionModification]
+ ) -> List[PartitionModification]:
+ action_key = [k for k, v in self._actions.items() if v == action][0]
+
+ match action_key:
+ case 'create_new_partition':
+ new_partition = self._create_new_partition()
+ data += [new_partition]
+ case 'suggest_partition_layout':
+ new_partitions = self._suggest_partition_layout(data)
+ if len(new_partitions) > 0:
+ data = new_partitions
+ case 'remove_added_partitions':
+ choice = self._reset_confirmation()
+ if choice.value == Menu.yes():
+ data = [part for part in data if part.is_exists_or_modify()]
+ case 'assign_mountpoint' if entry:
+ entry.mountpoint = self._prompt_mountpoint()
+ if entry.mountpoint == Path('/boot'):
+ entry.set_flag(PartitionFlag.Boot)
+ if SysInfo.has_uefi():
+ entry.set_flag(PartitionFlag.ESP)
+ case 'mark_formatting' if entry:
+ self._prompt_formatting(entry)
+ case 'mark_bootable' if entry:
+ entry.invert_flag(PartitionFlag.Boot)
+ if SysInfo.has_uefi():
+ entry.invert_flag(PartitionFlag.ESP)
+ case 'set_filesystem' if entry:
+ fs_type = self._prompt_partition_fs_type()
+ if fs_type:
+ entry.fs_type = fs_type
+ # btrfs subvolumes will define mountpoints
+ if fs_type == FilesystemType.Btrfs:
+ entry.mountpoint = None
+ case 'btrfs_mark_compressed' if entry:
+ self._toggle_mount_option(entry, BtrfsMountOption.compress)
+ case 'btrfs_mark_nodatacow' if entry:
+ self._toggle_mount_option(entry, BtrfsMountOption.nodatacow)
+ case 'btrfs_set_subvolumes' if entry:
+ self._set_btrfs_subvolumes(entry)
+ case 'delete_partition' if entry:
+ data = self._delete_partition(entry, data)
+
+ return data
+
+ def _delete_partition(
+ self,
+ entry: PartitionModification,
+ data: List[PartitionModification]
+ ) -> List[PartitionModification]:
+ if entry.is_exists_or_modify():
+ entry.status = ModificationStatus.Delete
+ return data
+ else:
+ return [d for d in data if d != entry]
+
+ def _toggle_mount_option(
+ self,
+ partition: PartitionModification,
+ option: BtrfsMountOption
+ ):
+ if option.value not in partition.mount_options:
+ if option == BtrfsMountOption.compress:
+ partition.mount_options = [
+ o for o in partition.mount_options
+ if o != BtrfsMountOption.nodatacow.value
+ ]
+
+ partition.mount_options = [
+ o for o in partition.mount_options
+ if not o.startswith(BtrfsMountOption.compress.name)
+ ]
+
+ partition.mount_options.append(option.value)
+ else:
+ partition.mount_options = [
+ o for o in partition.mount_options if o != option.value
+ ]
+
+ def _set_btrfs_subvolumes(self, partition: PartitionModification):
+ partition.btrfs_subvols = SubvolumeMenu(
+ _("Manage btrfs subvolumes for current partition"),
+ partition.btrfs_subvols
+ ).run()
+
+ def _prompt_formatting(self, partition: PartitionModification):
+ # an existing partition can toggle between Exist or Modify
+ if partition.is_modify():
+ partition.status = ModificationStatus.Exist
+ return
+ elif partition.exists():
+ partition.status = ModificationStatus.Modify
+
+ # If we mark a partition for formatting, but the format is CRYPTO LUKS, there's no point in formatting it really
+ # without asking the user which inner-filesystem they want to use. Since the flag 'encrypted' = True is already set,
+ # it's safe to change the filesystem for this partition.
+ if partition.fs_type == FilesystemType.Crypto_luks:
+ prompt = str(_('This partition is currently encrypted, to format it a filesystem has to be specified'))
+ fs_type = self._prompt_partition_fs_type(prompt)
+ partition.fs_type = fs_type
+
+ if fs_type == FilesystemType.Btrfs:
+ partition.mountpoint = None
+
+ def _prompt_mountpoint(self) -> Path:
+ header = str(_('Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + '\n'
+ header += str(_('If mountpoint /boot is set, then the partition will also be marked as bootable.')) + '\n'
+ prompt = str(_('Mountpoint: '))
+
+ print(header)
+
+ while True:
+ value = TextInput(prompt).run().strip()
+
+ if value:
+ mountpoint = Path(value)
+ break
+
+ return mountpoint
+
+ def _prompt_partition_fs_type(self, prompt: str = '') -> FilesystemType:
+ options = {fs.value: fs for fs in FilesystemType if fs != FilesystemType.Crypto_luks}
+
+ prompt = prompt + '\n' + str(_('Enter a desired filesystem type for the partition'))
+ choice = Menu(prompt, options, sort=False, skip=False).run()
+ return options[choice.single_value]
+
+ def _validate_value(
+ self,
+ sector_size: SectorSize,
+ total_size: Size,
+ text: str,
+ start: Optional[Size]
+ ) -> Optional[Size]:
+ match = re.match(r'([0-9]+)([a-zA-Z|%]*)', text, re.I)
+
+ if match:
+ str_value, unit = match.groups()
+
+ if unit == '%' and start:
+ available = total_size - start
+ value = int(available.value * (int(str_value) / 100))
+ unit = available.unit.name
+ else:
+ value = int(str_value)
+
+ if unit and unit not in Unit.get_all_units():
+ return None
+
+ unit = Unit[unit] if unit else Unit.sectors
+ return Size(value, unit, sector_size)
+
+ return None
+
+ def _enter_size(
+ self,
+ sector_size: SectorSize,
+ total_size: Size,
+ prompt: str,
+ default: Size,
+ start: Optional[Size],
+ ) -> Size:
+ while True:
+ value = TextInput(prompt).run().strip()
+ size: Optional[Size] = None
+ if not value:
+ size = default
+ else:
+ size = self._validate_value(sector_size, total_size, value, start)
+
+ if size:
+ return size
+
+ warn(f'Invalid value: {value}')
+
+ def _prompt_size(self) -> Tuple[Size, Size]:
+ device_info = self._device.device_info
+
+ text = str(_('Current free sectors on device {}:')).format(device_info.path) + '\n\n'
+ free_space_table = FormattedOutput.as_table(device_info.free_space_regions)
+ prompt = text + free_space_table + '\n'
+
+ total_sectors = device_info.total_size.format_size(Unit.sectors, device_info.sector_size)
+ total_bytes = device_info.total_size.format_size(Unit.B)
+
+ prompt += str(_('Total: {} / {}')).format(total_sectors, total_bytes) + '\n\n'
+ prompt += str(_('All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB...')) + '\n'
+ prompt += str(_('If no unit is provided, the value is interpreted as sectors')) + '\n'
+ print(prompt)
+
+ default_free_sector = self._find_default_free_space()
+
+ if not default_free_sector:
+ default_free_sector = DefaultFreeSector(
+ Size(0, Unit.sectors, self._device.device_info.sector_size),
+ Size(0, Unit.sectors, self._device.device_info.sector_size)
+ )
+
+ # prompt until a valid start sector was entered
+ start_prompt = str(_('Enter start (default: sector {}): ')).format(default_free_sector.start.value)
+
+ start_size = self._enter_size(
+ device_info.sector_size,
+ device_info.total_size,
+ start_prompt,
+ default_free_sector.start,
+ None
+ )
+
+ if start_size.value == default_free_sector.start.value and default_free_sector.end.value != 0:
+ end_size = default_free_sector.end
+ else:
+ end_size = device_info.total_size
+
+ # prompt until valid end sector was entered
+ end_prompt = str(_('Enter end (default: {}): ')).format(end_size.as_text())
+ end_size = self._enter_size(
+ device_info.sector_size,
+ device_info.total_size,
+ end_prompt,
+ end_size,
+ start_size
+ )
+
+ return start_size, end_size
+
+ def _find_default_free_space(self) -> Optional[DefaultFreeSector]:
+ device_info = self._device.device_info
+
+ largest_free_area: Optional[DeviceGeometry] = None
+ largest_deleted_area: Optional[PartitionModification] = None
+
+ if len(device_info.free_space_regions) > 0:
+ largest_free_area = max(device_info.free_space_regions, key=lambda r: r.get_length())
+
+ deleted_partitions = list(filter(lambda x: x.status == ModificationStatus.Delete, self._data))
+ if len(deleted_partitions) > 0:
+ largest_deleted_area = max(deleted_partitions, key=lambda p: p.length)
+
+ def _free_space(space: DeviceGeometry) -> DefaultFreeSector:
+ start = Size(space.start, Unit.sectors, device_info.sector_size)
+ end = Size(space.end, Unit.sectors, device_info.sector_size)
+ return DefaultFreeSector(start, end)
+
+ def _free_deleted(space: PartitionModification) -> DefaultFreeSector:
+ start = space.start.convert(Unit.sectors, self._device.device_info.sector_size)
+ end = space.end.convert(Unit.sectors, self._device.device_info.sector_size)
+ return DefaultFreeSector(start, end)
+
+ if not largest_deleted_area and largest_free_area:
+ return _free_space(largest_free_area)
+ elif not largest_free_area and largest_deleted_area:
+ return _free_deleted(largest_deleted_area)
+ elif not largest_deleted_area and not largest_free_area:
+ return None
+ elif largest_free_area and largest_deleted_area:
+ free_space = _free_space(largest_free_area)
+ if free_space.start > largest_deleted_area.start:
+ return free_space
+ else:
+ return _free_deleted(largest_deleted_area)
+
+ return None
+
+ def _create_new_partition(self) -> PartitionModification:
+ fs_type = self._prompt_partition_fs_type()
+
+ start_size, end_size = self._prompt_size()
+ length = end_size - start_size
+
+ # new line for the next prompt
+ print()
+
+ mountpoint = None
+ if fs_type != FilesystemType.Btrfs:
+ mountpoint = self._prompt_mountpoint()
+
+ partition = PartitionModification(
+ status=ModificationStatus.Create,
+ type=PartitionType.Primary,
+ start=start_size,
+ length=length,
+ fs_type=fs_type,
+ mountpoint=mountpoint
+ )
+
+ if partition.mountpoint == Path('/boot'):
+ partition.set_flag(PartitionFlag.Boot)
+ if SysInfo.has_uefi():
+ partition.set_flag(PartitionFlag.ESP)
+
+ return partition
+
+ def _reset_confirmation(self) -> MenuSelection:
+ prompt = str(_('This will remove all newly added partitions, continue?'))
+ choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run()
+ return choice
+
+ def _suggest_partition_layout(self, data: List[PartitionModification]) -> List[PartitionModification]:
+ # if modifications have been done already, inform the user
+ # that this operation will erase those modifications
+ if any([not entry.exists() for entry in data]):
+ choice = self._reset_confirmation()
+ if choice.value == Menu.no():
+ return []
+
+ from ..interactions.disk_conf import suggest_single_disk_layout
+
+ device_modification = suggest_single_disk_layout(self._device)
+ return device_modification.partitions
+
+
+def manual_partitioning(
+ device: BDevice,
+ prompt: str = '',
+ preset: List[PartitionModification] = []
+) -> List[PartitionModification]:
+ if not prompt:
+ prompt = str(_('Partition management: {}')).format(device.device_info.path) + '\n'
+ prompt += str(_('Total length: {}')).format(device.device_info.total_size.format_size(Unit.MiB))
+
+ manual_preset = []
+
+ if not preset:
+ # we'll display the existing partitions of the device
+ for partition in device.partition_infos:
+ manual_preset.append(
+ PartitionModification.from_existing_partition(partition)
+ )
+ else:
+ manual_preset = preset
+
+ menu_list = PartitioningList(prompt, device, manual_preset)
+ partitions: List[PartitionModification] = menu_list.run()
+
+ if menu_list.is_last_choice_cancel():
+ return preset
+
+ return partitions
diff --git a/archinstall/lib/disk/subvolume_menu.py b/archinstall/lib/disk/subvolume_menu.py
new file mode 100644
index 00000000..ea77149d
--- /dev/null
+++ b/archinstall/lib/disk/subvolume_menu.py
@@ -0,0 +1,61 @@
+from pathlib import Path
+from typing import List, Optional, Any, TYPE_CHECKING
+
+from .device_model import SubvolumeModification
+from ..menu import TextInput, ListManager
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class SubvolumeMenu(ListManager):
+ def __init__(self, prompt: str, btrfs_subvols: List[SubvolumeModification]):
+ self._actions = [
+ str(_('Add subvolume')),
+ str(_('Edit subvolume')),
+ str(_('Delete subvolume'))
+ ]
+ super().__init__(prompt, btrfs_subvols, [self._actions[0]], self._actions[1:])
+
+ def selected_action_display(self, subvolume: SubvolumeModification) -> str:
+ return str(subvolume.name)
+
+ def _add_subvolume(self, editing: Optional[SubvolumeModification] = None) -> Optional[SubvolumeModification]:
+ name = TextInput(f'\n\n{_("Subvolume name")}: ', editing.name if editing else '').run()
+
+ if not name:
+ return None
+
+ mountpoint = TextInput(f'{_("Subvolume mountpoint")}: ', str(editing.mountpoint) if editing else '').run()
+
+ if not mountpoint:
+ return None
+
+ return SubvolumeModification(Path(name), Path(mountpoint))
+
+ def handle_action(
+ self,
+ action: str,
+ entry: Optional[SubvolumeModification],
+ data: List[SubvolumeModification]
+ ) -> List[SubvolumeModification]:
+ if action == self._actions[0]: # add
+ new_subvolume = self._add_subvolume()
+
+ if new_subvolume is not None:
+ # in case a user with the same username as an existing user
+ # was created we'll replace the existing one
+ data = [d for d in data if d.name != new_subvolume.name]
+ data += [new_subvolume]
+ elif entry is not None:
+ if action == self._actions[1]: # edit subvolume
+ new_subvolume = self._add_subvolume(entry)
+
+ if new_subvolume is not None:
+ # we'll remove the original subvolume and add the modified version
+ data = [d for d in data if d.name != entry.name and d.name != new_subvolume.name]
+ data += [new_subvolume]
+ elif action == self._actions[2]: # delete
+ data = [d for d in data if d != entry]
+
+ return data
diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py
deleted file mode 100644
index 5809c073..00000000
--- a/archinstall/lib/disk/user_guides.py
+++ /dev/null
@@ -1,240 +0,0 @@
-from __future__ import annotations
-import logging
-from typing import Optional, Dict, Any, List, TYPE_CHECKING
-
-# https://stackoverflow.com/a/39757388/929999
-from ..models.subvolume import Subvolume
-
-if TYPE_CHECKING:
- from .blockdevice import BlockDevice
- _: Any
-
-from .helpers import sort_block_devices_based_on_performance, select_largest_device, select_disk_larger_than_or_close_to
-from ..hardware import has_uefi
-from ..output import log
-from ..menu import Menu
-
-
-def suggest_single_disk_layout(block_device :BlockDevice,
- default_filesystem :Optional[str] = None,
- advanced_options :bool = False) -> Dict[str, Any]:
-
- if not default_filesystem:
- from ..user_interaction import ask_for_main_filesystem_format
- default_filesystem = ask_for_main_filesystem_format(advanced_options)
-
- MIN_SIZE_TO_ALLOW_HOME_PART = 40 # GiB
- using_subvolumes = False
- using_home_partition = False
- compression = False
-
- if default_filesystem == 'btrfs':
- prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?'))
- choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
- using_subvolumes = choice.value == Menu.yes()
-
- prompt = str(_('Would you like to use BTRFS compression?'))
- choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
- compression = choice.value == Menu.yes()
-
- layout = {
- block_device.path : {
- "wipe" : True,
- "partitions" : []
- }
- }
-
- # Used for reference: https://wiki.archlinux.org/title/partitioning
-
- # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for
- # other bootloaders?
-
- # TODO: On BIOS, /boot partition is only needed if the drive will
- # be encrypted, otherwise it is not recommended. We should probably
- # add a check for whether the drive will be encrypted or not.
- layout[block_device.path]['partitions'].append({
- # Boot
- "type" : "primary",
- "start" : "3MiB",
- "size" : "203MiB",
- "boot" : True,
- "encrypted" : False,
- "wipe" : True,
- "mountpoint" : "/boot",
- "filesystem" : {
- "format" : "fat32"
- }
- })
-
- # Increase the UEFI partition if UEFI is detected.
- # Also re-align the start to 1MiB since we don't need the first sectors
- # like we do in MBR layouts where the boot loader is installed traditionally.
- if has_uefi():
- layout[block_device.path]['partitions'][-1]['start'] = '1MiB'
- layout[block_device.path]['partitions'][-1]['size'] = '512MiB'
-
- layout[block_device.path]['partitions'].append({
- # Root
- "type" : "primary",
- "start" : "206MiB",
- "encrypted" : False,
- "wipe" : True,
- "mountpoint" : "/" if not using_subvolumes else None,
- "filesystem" : {
- "format" : default_filesystem,
- "mount_options" : ["compress=zstd"] if compression else []
- }
- })
-
- if has_uefi():
- layout[block_device.path]['partitions'][-1]['start'] = '513MiB'
-
- if not using_subvolumes and block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART:
- prompt = str(_('Would you like to create a separate partition for /home?'))
- choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
- using_home_partition = choice.value == Menu.yes()
-
- # Set a size for / (/root)
- if using_subvolumes or block_device.size < MIN_SIZE_TO_ALLOW_HOME_PART or not using_home_partition:
- # We'll use subvolumes
- # Or the disk size is too small to allow for a separate /home
- # Or the user doesn't want to create a separate partition for /home
- layout[block_device.path]['partitions'][-1]['size'] = '100%'
- else:
- layout[block_device.path]['partitions'][-1]['size'] = f"{min(block_device.size, 20)}GiB"
-
- if default_filesystem == 'btrfs' and using_subvolumes:
- # if input('Do you want to use a recommended structure? (Y/n): ').strip().lower() in ('', 'y', 'yes'):
- # https://btrfs.wiki.kernel.org/index.php/FAQ
- # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash
- # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh
- layout[block_device.path]['partitions'][1]['btrfs'] = {
- 'subvolumes': [
- Subvolume('@', '/'),
- Subvolume('@home', '/home'),
- Subvolume('@log', '/var/log'),
- Subvolume('@pkg', '/var/cache/pacman/pkg'),
- Subvolume('@.snapshots', '/.snapshots')
- ]
- }
- elif using_home_partition:
- # If we don't want to use subvolumes,
- # But we want to be able to re-use data between re-installs..
- # A second partition for /home would be nice if we have the space for it
- layout[block_device.path]['partitions'].append({
- # Home
- "type" : "primary",
- "start" : f"{min(block_device.size, 20)}GiB",
- "size" : "100%",
- "encrypted" : False,
- "wipe" : True,
- "mountpoint" : "/home",
- "filesystem" : {
- "format" : default_filesystem,
- "mount_options" : ["compress=zstd"] if compression else []
- }
- })
-
- return layout
-
-
-def suggest_multi_disk_layout(block_devices :List[BlockDevice], default_filesystem :Optional[str] = None, advanced_options :bool = False):
-
- if not default_filesystem:
- from ..user_interaction import ask_for_main_filesystem_format
- default_filesystem = ask_for_main_filesystem_format(advanced_options)
-
- # Not really a rock solid foundation of information to stand on, but it's a start:
- # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/
- # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/
-
- MIN_SIZE_TO_ALLOW_HOME_PART = 40 # GiB
- ARCH_LINUX_INSTALLED_SIZE = 20 # GiB, rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size?
-
- block_devices = sort_block_devices_based_on_performance(block_devices).keys()
-
- home_device = select_largest_device(block_devices, gigabytes=MIN_SIZE_TO_ALLOW_HOME_PART)
- root_device = select_disk_larger_than_or_close_to(block_devices, gigabytes=ARCH_LINUX_INSTALLED_SIZE, filter_out=[home_device])
-
- if home_device is None or root_device is None:
- text = _('The selected drives do not have the minimum capacity required for an automatic suggestion\n')
- text += _('Minimum capacity for /home partition: {}GB\n').format(MIN_SIZE_TO_ALLOW_HOME_PART)
- text += _('Minimum capacity for Arch Linux partition: {}GB').format(ARCH_LINUX_INSTALLED_SIZE)
- Menu(str(text), [str(_('Continue'))], skip=False).run()
- return None
-
- compression = False
-
- if default_filesystem == 'btrfs':
- # prompt = 'Would you like to use BTRFS subvolumes with a default structure?'
- # choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run()
- # using_subvolumes = choice == 'yes'
-
- prompt = str(_('Would you like to use BTRFS compression?'))
- choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
- compression = choice.value == Menu.yes()
-
- log(f"Suggesting multi-disk-layout using {len(block_devices)} disks, where {root_device} will be /root and {home_device} will be /home", level=logging.DEBUG)
-
- layout = {
- root_device.path : {
- "wipe" : True,
- "partitions" : []
- },
- home_device.path : {
- "wipe" : True,
- "partitions" : []
- },
- }
-
- # TODO: Same deal as with the single disk layout, we should
- # probably check if the drive will be encrypted.
- layout[root_device.path]['partitions'].append({
- # Boot
- "type" : "primary",
- "start" : "3MiB",
- "size" : "203MiB",
- "boot" : True,
- "encrypted" : False,
- "wipe" : True,
- "mountpoint" : "/boot",
- "filesystem" : {
- "format" : "fat32"
- }
- })
-
- if has_uefi():
- layout[root_device.path]['partitions'][-1]['start'] = '1MiB'
- layout[root_device.path]['partitions'][-1]['size'] = '512MiB'
-
- layout[root_device.path]['partitions'].append({
- # Root
- "type" : "primary",
- "start" : "206MiB",
- "size" : "100%",
- "encrypted" : False,
- "wipe" : True,
- "mountpoint" : "/",
- "filesystem" : {
- "format" : default_filesystem,
- "mount_options" : ["compress=zstd"] if compression else []
- }
- })
- if has_uefi():
- layout[root_device.path]['partitions'][-1]['start'] = '513MiB'
-
- layout[home_device.path]['partitions'].append({
- # Home
- "type" : "primary",
- "start" : "1MiB",
- "size" : "100%",
- "encrypted" : False,
- "wipe" : True,
- "mountpoint" : "/home",
- "filesystem" : {
- "format" : default_filesystem,
- "mount_options" : ["compress=zstd"] if compression else []
- }
- })
-
- return layout
diff --git a/archinstall/lib/disk/validators.py b/archinstall/lib/disk/validators.py
deleted file mode 100644
index 076a8ba2..00000000
--- a/archinstall/lib/disk/validators.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from typing import List
-
-def valid_parted_position(pos :str) -> bool:
- if not len(pos):
- return False
-
- if pos.isdigit():
- return True
-
- pos_lower = pos.lower()
-
- if (pos_lower.endswith('b') or pos_lower.endswith('s')) and pos[:-1].isdigit():
- return True
-
- if any(pos_lower.endswith(size) and pos[:-len(size)].replace(".", "", 1).isdigit()
- for size in ['%', 'kb', 'mb', 'gb', 'tb', 'kib', 'mib', 'gib', 'tib']):
- return True
-
- return False
-
-
-def fs_types() -> List[str]:
- # https://www.gnu.org/software/parted/manual/html_node/mkpart.html
- # Above link doesn't agree with `man parted` /mkpart documentation:
- """
- fs-type can
- be one of "btrfs", "ext2",
- "ext3", "ext4", "fat16",
- "fat32", "hfs", "hfs+",
- "linux-swap", "ntfs", "reisâ€
- erfs", "udf", or "xfs".
- """
- return [
- "btrfs",
- "ext2",
- "ext3", "ext4", # `man parted` allows these
- "fat16", "fat32",
- "hfs", "hfs+", # "hfsx", not included in `man parted`
- "linux-swap",
- "ntfs",
- "reiserfs",
- "udf", # "ufs", not included in `man parted`
- "xfs", # `man parted` allows this
- ]
-
-
-def valid_fs_type(fstype :str) -> bool:
- return fstype.lower() in fs_types()
diff --git a/archinstall/lib/exceptions.py b/archinstall/lib/exceptions.py
index a66e4e04..80926e0b 100644
--- a/archinstall/lib/exceptions.py
+++ b/archinstall/lib/exceptions.py
@@ -3,23 +3,20 @@ from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from .general import SysCommandWorker
-class RequirementError(BaseException):
- pass
-
-class DiskError(BaseException):
+class RequirementError(Exception):
pass
-class UnknownFilesystemFormat(BaseException):
+class DiskError(Exception):
pass
-class ProfileError(BaseException):
+class UnknownFilesystemFormat(Exception):
pass
-class SysCallError(BaseException):
+class SysCallError(Exception):
def __init__(self, message :str, exit_code :Optional[int] = None, worker :Optional['SysCommandWorker'] = None) -> None:
super(SysCallError, self).__init__(message)
self.message = message
@@ -27,33 +24,17 @@ class SysCallError(BaseException):
self.worker = worker
-class PermissionError(BaseException):
+class HardwareIncompatibilityError(Exception):
pass
-class ProfileNotFound(BaseException):
+class ServiceException(Exception):
pass
-class HardwareIncompatibilityError(BaseException):
+class PackageError(Exception):
pass
-class UserError(BaseException):
+class Deprecated(Exception):
pass
-
-
-class ServiceException(BaseException):
- pass
-
-
-class PackageError(BaseException):
- pass
-
-
-class TranslationError(BaseException):
- pass
-
-
-class Deprecated(BaseException):
- pass \ No newline at end of file
diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py
index 79ab024b..8dbf23ff 100644
--- a/archinstall/lib/general.py
+++ b/archinstall/lib/general.py
@@ -1,7 +1,6 @@
from __future__ import annotations
-import hashlib
+
import json
-import logging
import os
import secrets
import shlex
@@ -12,212 +11,117 @@ import sys
import time
import re
import urllib.parse
-import urllib.request
+from urllib.request import Request, urlopen
import urllib.error
import pathlib
from datetime import datetime, date
+from enum import Enum
from typing import Callable, Optional, Dict, Any, List, Union, Iterator, TYPE_CHECKING
-# https://stackoverflow.com/a/39757388/929999
-if TYPE_CHECKING:
- from .installer import Installer
-
-if sys.platform == 'linux':
- from select import epoll, EPOLLIN, EPOLLHUP
-else:
- import select
- EPOLLIN = 0
- EPOLLHUP = 0
-
- class epoll():
- """ #!if windows
- Create a epoll() implementation that simulates the epoll() behavior.
- This so that the rest of the code doesn't need to worry weither we're using select() or epoll().
- """
- def __init__(self) -> None:
- self.sockets: Dict[str, Any] = {}
- self.monitoring: Dict[int, Any] = {}
-
- def unregister(self, fileno :int, *args :List[Any], **kwargs :Dict[str, Any]) -> None:
- try:
- del(self.monitoring[fileno]) # noqa: E275
- except:
- pass
-
- def register(self, fileno :int, *args :int, **kwargs :Dict[str, Any]) -> None:
- self.monitoring[fileno] = True
-
- def poll(self, timeout: float = 0.05, *args :str, **kwargs :Dict[str, Any]) -> List[Any]:
- try:
- return [[fileno, 1] for fileno in select.select(list(self.monitoring.keys()), [], [], timeout)[0]]
- except OSError:
- return []
+from select import epoll, EPOLLIN, EPOLLHUP
+from shutil import which
from .exceptions import RequirementError, SysCallError
-from .output import log
+from .output import debug, error, info
from .storage import storage
-def gen_uid(entropy_length :int = 256) -> str:
- return hashlib.sha512(os.urandom(entropy_length)).hexdigest()
+
+if TYPE_CHECKING:
+ from .installer import Installer
+
def generate_password(length :int = 64) -> str:
- haystack = string.printable # digits, ascii_letters, punctiation (!"#$[] etc) and whitespace
+ haystack = string.printable # digits, ascii_letters, punctuation (!"#$[] etc) and whitespace
return ''.join(secrets.choice(haystack) for i in range(length))
-def multisplit(s :str, splitters :List[str]) -> str:
- s = [s, ]
- for key in splitters:
- ns = []
- for obj in s:
- x = obj.split(key)
- for index, part in enumerate(x):
- if len(part):
- ns.append(part)
- if index < len(x) - 1:
- ns.append(key)
- s = ns
- return s
def locate_binary(name :str) -> str:
- for PATH in os.environ['PATH'].split(':'):
- for root, folders, files in os.walk(PATH):
- for file in files:
- if file == name:
- return os.path.join(root, file)
- break # Don't recurse
-
+ if path := which(name):
+ return path
raise RequirementError(f"Binary {name} does not exist.")
-def clear_vt100_escape_codes(data :Union[bytes, str]):
- # https://stackoverflow.com/a/43627833/929999
- if type(data) == bytes:
- vt100_escape_regex = bytes(r'\x1B\[[?0-9;]*[a-zA-Z]', 'UTF-8')
- else:
- vt100_escape_regex = r'\x1B\[[?0-9;]*[a-zA-Z]'
-
- for match in re.findall(vt100_escape_regex, data, re.IGNORECASE):
- data = data.replace(match, '' if type(data) == str else b'')
- return data
-
-def json_dumps(*args :str, **kwargs :str) -> str:
- return json.dumps(*args, **{**kwargs, 'cls': JSON})
+def clear_vt100_escape_codes(data :Union[bytes, str]) -> Union[bytes, str]:
+ # https://stackoverflow.com/a/43627833/929999
+ vt100_escape_regex = r'\x1B\[[?0-9;]*[a-zA-Z]'
+ if isinstance(data, bytes):
+ return re.sub(vt100_escape_regex.encode(), b'', data)
+ return re.sub(vt100_escape_regex, '', data)
-class JsonEncoder:
- @staticmethod
- def _encode(obj :Any) -> Any:
- """
- This JSON encoder function will try it's best to convert
- any archinstall data structures, instances or variables into
- something that's understandable by the json.parse()/json.loads() lib.
- _encode() will skip any dictionary key starting with an exclamation mark (!)
- """
- if isinstance(obj, dict):
- # We'll need to iterate not just the value that default() usually gets passed
- # But also iterate manually over each key: value pair in order to trap the keys.
-
- copy = {}
- for key, val in list(obj.items()):
- if isinstance(val, dict):
- # This, is a EXTREMELY ugly hack.. but it's the only quick way I can think of to trigger a encoding of sub-dictionaries.
- val = json.loads(json.dumps(val, cls=JSON))
- else:
- val = JsonEncoder._encode(val)
-
- if type(key) == str and key[0] == '!':
- pass
- else:
- copy[JsonEncoder._encode(key)] = val
- return copy
- elif hasattr(obj, 'json'):
- # json() is a friendly name for json-helper, it should return
- # a dictionary representation of the object so that it can be
- # processed by the json library.
- return json.loads(json.dumps(obj.json(), cls=JSON))
- elif hasattr(obj, '__dump__'):
- return obj.__dump__()
- elif isinstance(obj, (datetime, date)):
- return obj.isoformat()
- elif isinstance(obj, (list, set, tuple)):
- return [json.loads(json.dumps(item, cls=JSON)) for item in obj]
- elif isinstance(obj, (pathlib.Path)):
- return str(obj)
- else:
- return obj
+def jsonify(obj: Any, safe: bool = True) -> Any:
+ """
+ Converts objects into json.dumps() compatible nested dictionaries.
+ Setting safe to True skips dictionary keys starting with a bang (!)
+ """
- @staticmethod
- def _unsafe_encode(obj :Any) -> Any:
- """
- Same as _encode() but it keeps dictionary keys starting with !
- """
- if isinstance(obj, dict):
- copy = {}
- for key, val in list(obj.items()):
- if isinstance(val, dict):
- # This, is a EXTREMELY ugly hack.. but it's the only quick way I can think of to trigger a encoding of sub-dictionaries.
- val = json.loads(json.dumps(val, cls=UNSAFE_JSON))
- else:
- val = JsonEncoder._unsafe_encode(val)
-
- copy[JsonEncoder._unsafe_encode(key)] = val
- return copy
- else:
- return JsonEncoder._encode(obj)
+ compatible_types = str, int, float, bool
+ if isinstance(obj, dict):
+ return {
+ key: jsonify(value, safe)
+ for key, value in obj.items()
+ if isinstance(key, compatible_types)
+ and not (isinstance(key, str) and key.startswith("!") and safe)
+ }
+ if isinstance(obj, Enum):
+ return obj.value
+ if hasattr(obj, 'json'):
+ # json() is a friendly name for json-helper, it should return
+ # a dictionary representation of the object so that it can be
+ # processed by the json library.
+ return jsonify(obj.json(), safe)
+ if isinstance(obj, (datetime, date)):
+ return obj.isoformat()
+ if isinstance(obj, (list, set, tuple)):
+ return [jsonify(item, safe) for item in obj]
+ if isinstance(obj, pathlib.Path):
+ return str(obj)
+ if hasattr(obj, "__dict__"):
+ return vars(obj)
+
+ return obj
class JSON(json.JSONEncoder, json.JSONDecoder):
"""
A safe JSON encoder that will omit private information in dicts (starting with !)
"""
- def _encode(self, obj :Any) -> Any:
- return JsonEncoder._encode(obj)
- def encode(self, obj :Any) -> Any:
- return super(JSON, self).encode(self._encode(obj))
+ def encode(self, obj: Any) -> str:
+ return super().encode(jsonify(obj))
+
class UNSAFE_JSON(json.JSONEncoder, json.JSONDecoder):
"""
UNSAFE_JSON will call/encode and keep private information in dicts (starting with !)
"""
- def _encode(self, obj :Any) -> Any:
- return JsonEncoder._unsafe_encode(obj)
- def encode(self, obj :Any) -> Any:
- return super(UNSAFE_JSON, self).encode(self._encode(obj))
+ def encode(self, obj: Any) -> str:
+ return super().encode(jsonify(obj, safe=False))
+
class SysCommandWorker:
- def __init__(self,
+ def __init__(
+ self,
cmd :Union[str, List[str]],
callbacks :Optional[Dict[str, Any]] = None,
peek_output :Optional[bool] = False,
- peak_output :Optional[bool] = False,
environment_vars :Optional[Dict[str, Any]] = None,
logfile :Optional[None] = None,
working_directory :Optional[str] = './',
- remove_vt100_escape_codes_from_lines :bool = True):
-
- if peak_output:
- log("SysCommandWorker()'s peak_output is deprecated, use peek_output instead.", level=logging.WARNING, fg='red')
-
- if not callbacks:
- callbacks = {}
- if not environment_vars:
- environment_vars = {}
+ remove_vt100_escape_codes_from_lines :bool = True
+ ):
+ callbacks = callbacks or {}
+ environment_vars = environment_vars or {}
- if type(cmd) is str:
+ if isinstance(cmd, str):
cmd = shlex.split(cmd)
- cmd = list(cmd) # This is to please mypy
- if cmd[0][0] != '/' and cmd[0][:2] != './':
- # "which" doesn't work as it's a builtin to bash.
- # It used to work, but for whatever reason it doesn't anymore.
- # We there for fall back on manual lookup in os.PATH
- cmd[0] = locate_binary(cmd[0])
+ if cmd:
+ if cmd[0][0] != '/' and cmd[0][:2] != './': # pathlib.Path does not work well
+ cmd[0] = locate_binary(cmd[0])
self.cmd = cmd
self.callbacks = callbacks
self.peek_output = peek_output
- if not self.peek_output and peak_output:
- self.peek_output = peak_output
# define the standard locale for command outputs. For now the C ascii one. Can be overridden
self.environment_vars = {**storage.get('CMD_LOCALE',{}),**environment_vars}
self.logfile = logfile
@@ -237,27 +141,36 @@ class SysCommandWorker:
Contains will also move the current buffert position forward.
This is to avoid re-checking the same data when looking for output.
"""
- assert type(key) == bytes
+ assert isinstance(key, bytes)
- if (contains := key in self._trace_log[self._trace_log_pos:]):
- self._trace_log_pos += self._trace_log[self._trace_log_pos:].find(key) + len(key)
+ index = self._trace_log.find(key, self._trace_log_pos)
+ if index >= 0:
+ self._trace_log_pos += index + len(key)
+ return True
- return contains
+ return False
def __iter__(self, *args :str, **kwargs :Dict[str, Any]) -> Iterator[bytes]:
- for line in self._trace_log[self._trace_log_pos:self._trace_log.rfind(b'\n')].split(b'\n'):
- if line:
- if self.remove_vt100_escape_codes_from_lines:
- line = clear_vt100_escape_codes(line)
+ 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:
+ if self.remove_vt100_escape_codes_from_lines:
+ line = clear_vt100_escape_codes(line) # type: ignore
- yield line + b'\n'
+ yield line + b'\n'
- self._trace_log_pos = self._trace_log.rfind(b'\n')
+ self._trace_log_pos = last_line
def __repr__(self) -> str:
self.make_sure_we_are_executing()
return str(self._trace_log)
+ def __str__(self) -> str:
+ try:
+ return self._trace_log.decode('utf-8')
+ except UnicodeDecodeError:
+ return str(self._trace_log)
+
def __enter__(self) -> 'SysCommandWorker':
return self
@@ -278,10 +191,14 @@ class SysCommandWorker:
sys.stdout.flush()
if len(args) >= 2 and args[1]:
- log(args[1], level=logging.DEBUG, fg='red')
+ debug(args[1])
if self.exit_code != 0:
- raise SysCallError(f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {self._trace_log[-500:]}", self.exit_code, worker=self)
+ raise SysCallError(
+ f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {str(self)[-500:]}",
+ self.exit_code,
+ worker=self
+ )
def is_alive(self) -> bool:
self.poll()
@@ -292,12 +209,13 @@ class SysCommandWorker:
return False
def write(self, data: bytes, line_ending :bool = True) -> int:
- assert type(data) == bytes # TODO: Maybe we can support str as well and encode it
+ assert isinstance(data, bytes) # TODO: Maybe we can support str as well and encode it
self.make_sure_we_are_executing()
if self.child_fd:
return os.write(self.child_fd, data + (b'\n' if line_ending else b''))
+ os.fsync(self.child_fd)
return 0
@@ -317,7 +235,7 @@ class SysCommandWorker:
def peak(self, output: Union[str, bytes]) -> bool:
if self.peek_output:
- if type(output) == bytes:
+ if isinstance(output, bytes):
try:
output = output.decode('UTF-8')
except UnicodeDecodeError:
@@ -330,7 +248,7 @@ class SysCommandWorker:
change_perm = True
with peak_logfile.open("a") as peek_output_log:
- peek_output_log.write(output)
+ peek_output_log.write(str(output))
if change_perm:
os.chmod(str(peak_logfile), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
@@ -355,7 +273,7 @@ class SysCommandWorker:
self.ended = time.time()
break
- if self.ended or (got_output is False and pid_exists(self.pid) is False):
+ if self.ended or (not got_output and not _pid_exists(self.pid)):
self.ended = time.time()
try:
wait_status = os.waitpid(self.pid, 0)[1]
@@ -394,22 +312,20 @@ class SysCommandWorker:
if change_perm:
os.chmod(str(history_logfile), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
- except PermissionError:
- pass
- # If history_logfile does not exist, ignore the error
- except FileNotFoundError:
+ except (PermissionError, FileNotFoundError):
+ # If history_logfile does not exist, ignore the error
pass
except Exception as e:
exception_type = type(e).__name__
- log(f"Unexpected {exception_type} occurred in {self.cmd}: {e}", level=logging.ERROR)
+ error(f"Unexpected {exception_type} occurred in {self.cmd}: {e}")
raise e
os.execve(self.cmd[0], list(self.cmd), {**os.environ, **self.environment_vars})
if storage['arguments'].get('debug'):
- log(f"Executing: {self.cmd}", level=logging.DEBUG)
+ debug(f"Executing: {self.cmd}")
except FileNotFoundError:
- log(f"{self.cmd[0]} does not exist.", level=logging.ERROR, fg="red")
+ error(f"{self.cmd[0]} does not exist.")
self.exit_code = 1
return False
else:
@@ -428,29 +344,19 @@ class SysCommandWorker:
class SysCommand:
def __init__(self,
cmd :Union[str, List[str]],
- callbacks :Optional[Dict[str, Callable[[Any], Any]]] = None,
+ callbacks :Dict[str, Callable[[Any], Any]] = {},
start_callback :Optional[Callable[[Any], Any]] = None,
peek_output :Optional[bool] = False,
- peak_output :Optional[bool] = False,
environment_vars :Optional[Dict[str, Any]] = None,
working_directory :Optional[str] = './',
remove_vt100_escape_codes_from_lines :bool = True):
- if peak_output:
- log("SysCommandWorker()'s peak_output is deprecated, use peek_output instead.", level=logging.WARNING, fg='red')
-
- _callbacks = {}
- if callbacks:
- for hook, func in callbacks.items():
- _callbacks[hook] = func
+ self._callbacks = callbacks.copy()
if start_callback:
- _callbacks['on_start'] = start_callback
+ self._callbacks['on_start'] = start_callback
self.cmd = cmd
- self._callbacks = _callbacks
self.peek_output = peek_output
- if not self.peek_output and peak_output:
- self.peek_output = peak_output
self.environment_vars = environment_vars
self.working_directory = working_directory
self.remove_vt100_escape_codes_from_lines = remove_vt100_escape_codes_from_lines
@@ -466,7 +372,7 @@ class SysCommand:
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
if len(args) >= 2 and args[1]:
- log(args[1], level=logging.ERROR, fg='red')
+ error(args[1])
def __iter__(self, *args :List[Any], **kwargs :Dict[str, Any]) -> Iterator[bytes]:
if self.session:
@@ -477,17 +383,15 @@ class SysCommand:
if not self.session:
raise KeyError(f"SysCommand() does not have an active session.")
elif type(key) is slice:
- start = key.start if key.start else 0
- end = key.stop if key.stop else len(self.session._trace_log)
+ start = key.start or 0
+ end = key.stop or len(self.session._trace_log)
return self.session._trace_log[start:end]
else:
raise ValueError("SysCommand() doesn't have key & value pairs, only slices, SysCommand('ls')[:10] as an example.")
def __repr__(self, *args :List[Any], **kwargs :Dict[str, Any]) -> str:
- if self.session:
- return self.session._trace_log.decode('UTF-8', errors='backslashreplace')
- return ''
+ return self.decode('UTF-8', errors='backslashreplace') or ''
def __json__(self) -> Dict[str, Union[str, bool, List[str], Dict[str, Any], Optional[bool], Optional[Dict[str, Any]]]]:
return {
@@ -495,7 +399,7 @@ class SysCommand:
'callbacks': self._callbacks,
'peak': self.peek_output,
'environment_vars': self.environment_vars,
- 'session': True if self.session else False
+ 'session': self.session is not None
}
def create_session(self) -> bool:
@@ -505,7 +409,7 @@ class SysCommand:
clears any printed output if ``.peek_output=True``.
"""
if self.session:
- return self.session
+ return True
with SysCommandWorker(
self.cmd,
@@ -515,10 +419,9 @@ class SysCommand:
remove_vt100_escape_codes_from_lines=self.remove_vt100_escape_codes_from_lines,
working_directory=self.working_directory) as session:
- if not self.session:
- self.session = session
+ self.session = session
- while self.session.ended is None:
+ while not self.session.ended:
self.session.poll()
if self.peek_output:
@@ -527,10 +430,21 @@ class SysCommand:
return True
- def decode(self, fmt :str = 'UTF-8') -> Optional[str]:
- if self.session:
- return self.session._trace_log.decode(fmt)
- return None
+ def decode(self, encoding: str = 'utf-8', errors='backslashreplace', strip: bool = True) -> str:
+ if not self.session:
+ raise ValueError('No session available to decode')
+
+ val = self.session._trace_log.decode(encoding, errors=errors)
+
+ if strip:
+ return val.strip()
+ return val
+
+ def output(self) -> bytes:
+ if not self.session:
+ raise ValueError('No session available')
+
+ return self.session._trace_log.replace(b'\r\n', b'\n')
@property
def exit_code(self) -> Optional[int]:
@@ -546,22 +460,7 @@ class SysCommand:
return None
-def prerequisite_check() -> bool:
- """
- This function is used as a safety check before
- continuing with an installation.
-
- Could be anything from checking that /boot is big enough
- to check if nvidia hardware exists when nvidia driver was chosen.
- """
-
- return True
-
-def reboot():
- SysCommand("/usr/bin/reboot")
-
-
-def pid_exists(pid: int) -> bool:
+def _pid_exists(pid: int) -> bool:
try:
return any(subprocess.check_output(['/usr/bin/ps', '--no-headers', '-o', 'pid', '-p', str(pid)]).strip())
except subprocess.CalledProcessError:
@@ -570,56 +469,57 @@ def pid_exists(pid: int) -> bool:
def run_custom_user_commands(commands :List[str], installation :Installer) -> None:
for index, command in enumerate(commands):
- log(f'Executing custom command "{command}" ...', level=logging.INFO)
+ script_path = f"/var/tmp/user-command.{index}.sh"
+ chroot_path = f"{installation.target}/{script_path}"
- with open(f"{installation.target}/var/tmp/user-command.{index}.sh", "w") as temp_script:
- temp_script.write(command)
+ info(f'Executing custom command "{command}" ...')
+ with open(chroot_path, "w") as user_script:
+ user_script.write(command)
- execution_output = SysCommand(f"arch-chroot {installation.target} bash /var/tmp/user-command.{index}.sh")
+ SysCommand(f"arch-chroot {installation.target} bash {script_path}")
+
+ os.unlink(chroot_path)
- log(execution_output)
- os.unlink(f"{installation.target}/var/tmp/user-command.{index}.sh")
def json_stream_to_structure(configuration_identifier : str, stream :str, target :dict) -> bool :
"""
- Function to load a stream (file (as name) or valid JSON string into an existing dictionary
- Returns true if it could be done
- Return false if operation could not be executed
+ Load a JSON encoded dictionary from a stream and merge it into an existing dictionary.
+ A stream can be a filepath, a URL or a raw JSON string.
+ Returns True if the operation succeeded, False otherwise.
+configuration_identifier is just a parameter to get meaningful, but not so long messages
"""
- parsed_url = urllib.parse.urlparse(stream)
+ raw: Optional[str] = None
+ # Try using the stream as a URL that should be grabbed
+ if urllib.parse.urlparse(stream).scheme:
+ try:
+ with urlopen(Request(stream, headers={'User-Agent': 'ArchInstall'})) as response:
+ raw = response.read()
+ except urllib.error.HTTPError as err:
+ error(f"Could not fetch JSON from {stream} as {configuration_identifier}: {err}")
+ return False
- if parsed_url.scheme: # The stream is in fact a URL that should be grabbed
+ # Try using the stream as a filepath that should be read
+ if raw is None and (path := pathlib.Path(stream)).exists():
try:
- with urllib.request.urlopen(urllib.request.Request(stream, headers={'User-Agent': 'ArchInstall'})) as response:
- target.update(json.loads(response.read()))
- except urllib.error.HTTPError as error:
- log(f"Could not load {configuration_identifier} via {parsed_url} due to: {error}", level=logging.ERROR, fg="red")
+ raw = path.read_text()
+ except Exception as err:
+ error(f"Could not read file {stream} as {configuration_identifier}: {err}")
return False
- else:
- if pathlib.Path(stream).exists():
- try:
- with pathlib.Path(stream).open() as fh:
- target.update(json.load(fh))
- except Exception as error:
- log(f"{configuration_identifier} = {stream} does not contain a valid JSON format: {error}", level=logging.ERROR, fg="red")
- return False
- else:
- # NOTE: This is a rudimentary check if what we're trying parse is a dict structure.
- # Which is the only structure we tolerate anyway.
- if stream.strip().startswith('{') and stream.strip().endswith('}'):
- try:
- target.update(json.loads(stream))
- except Exception as e:
- log(f" {configuration_identifier} Contains an invalid JSON format : {e}",level=logging.ERROR, fg="red")
- return False
- else:
- log(f" {configuration_identifier} is neither a file nor is a JSON string:",level=logging.ERROR, fg="red")
- return False
+ try:
+ # We use `or` to try the stream as raw JSON to be parsed
+ structure = json.loads(raw or stream)
+ except Exception as err:
+ error(f"{configuration_identifier} contains an invalid JSON format: {err}")
+ return False
+ if not isinstance(structure, dict):
+ error(f"{stream} passed as {configuration_identifier} is not a JSON encoded dictionary")
+ return False
+ target.update(structure)
return True
+
def secret(x :str):
""" return * with len equal to to the input string """
return '*' * len(x)
diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py
new file mode 100644
index 00000000..1b5e779b
--- /dev/null
+++ b/archinstall/lib/global_menu.py
@@ -0,0 +1,472 @@
+from __future__ import annotations
+
+from typing import Any, List, Optional, Dict, TYPE_CHECKING
+
+from . import disk
+from .general import secret
+from .hardware import SysInfo
+from .locale.locale_menu import LocaleConfiguration, LocaleMenu
+from .menu import Selector, AbstractMenu
+from .mirrors import MirrorConfiguration, MirrorMenu
+from .models import NetworkConfiguration, NicType
+from .models.bootloader import Bootloader
+from .models.audio_configuration import Audio, AudioConfiguration
+from .models.users import User
+from .output import FormattedOutput
+from .profile.profile_menu import ProfileConfiguration
+from .configuration import save_config
+from .interactions import add_number_of_parallel_downloads
+from .interactions import ask_additional_packages_to_install
+from .interactions import ask_for_additional_users
+from .interactions import ask_for_audio_selection
+from .interactions import ask_for_bootloader
+from .interactions import ask_for_uki
+from .interactions import ask_for_swap
+from .interactions import ask_hostname
+from .interactions import ask_to_configure_network
+from .interactions import get_password, ask_for_a_timezone
+from .interactions import select_additional_repositories
+from .interactions import select_kernel
+from .utils.util import format_cols
+from .interactions import ask_ntp
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class GlobalMenu(AbstractMenu):
+ def __init__(self, data_store: Dict[str, Any]):
+ super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3)
+
+ def setup_selection_menu_options(self):
+ # archinstall.Language will not use preset values
+ self._menu_options['archinstall-language'] = \
+ Selector(
+ _('Archinstall language'),
+ lambda x: self._select_archinstall_language(x),
+ display_func=lambda x: x.display_name,
+ default=self.translation_handler.get_language_by_abbr('en'))
+ self._menu_options['locale_config'] = \
+ Selector(
+ _('Locales'),
+ lambda preset: self._locale_selection(preset),
+ preview_func=self._prev_locale,
+ display_func=lambda x: self.defined_text if x else '')
+ self._menu_options['mirror_config'] = \
+ Selector(
+ _('Mirrors'),
+ lambda preset: self._mirror_configuration(preset),
+ display_func=lambda x: self.defined_text if x else '',
+ preview_func=self._prev_mirror_config
+ )
+ self._menu_options['disk_config'] = \
+ Selector(
+ _('Disk configuration'),
+ lambda preset: self._select_disk_config(preset),
+ preview_func=self._prev_disk_config,
+ display_func=lambda x: self.defined_text if x else '',
+ )
+ self._menu_options['disk_encryption'] = \
+ Selector(
+ _('Disk encryption'),
+ lambda preset: self._disk_encryption(preset),
+ preview_func=self._prev_disk_encryption,
+ display_func=lambda x: self._display_disk_encryption(x),
+ dependencies=['disk_config']
+ )
+ self._menu_options['swap'] = \
+ Selector(
+ _('Swap'),
+ lambda preset: ask_for_swap(preset),
+ default=True)
+ self._menu_options['bootloader'] = \
+ Selector(
+ _('Bootloader'),
+ lambda preset: ask_for_bootloader(preset),
+ display_func=lambda x: x.value,
+ default=Bootloader.get_default())
+ self._menu_options['uki'] = \
+ Selector(
+ _('Unified kernel images'),
+ lambda preset: ask_for_uki(preset),
+ default=False)
+ self._menu_options['hostname'] = \
+ Selector(
+ _('Hostname'),
+ lambda preset: ask_hostname(preset),
+ default='archlinux')
+ # root password won't have preset value
+ self._menu_options['!root-password'] = \
+ Selector(
+ _('Root password'),
+ lambda preset:self._set_root_password(),
+ display_func=lambda x: secret(x) if x else '')
+ self._menu_options['!users'] = \
+ Selector(
+ _('User account'),
+ lambda x: self._create_user_account(x),
+ default=[],
+ display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else '',
+ preview_func=self._prev_users)
+ self._menu_options['profile_config'] = \
+ Selector(
+ _('Profile'),
+ lambda preset: self._select_profile(preset),
+ display_func=lambda x: x.profile.name if x else '',
+ preview_func=self._prev_profile
+ )
+ self._menu_options['audio_config'] = \
+ Selector(
+ _('Audio'),
+ lambda preset: self._select_audio(preset),
+ display_func=lambda x: self._display_audio(x)
+ )
+ self._menu_options['parallel downloads'] = \
+ Selector(
+ _('Parallel Downloads'),
+ lambda preset: add_number_of_parallel_downloads(preset),
+ display_func=lambda x: x if x else '0',
+ default=0
+ )
+ self._menu_options['kernels'] = \
+ Selector(
+ _('Kernels'),
+ lambda preset: select_kernel(preset),
+ display_func=lambda x: ', '.join(x) if x else None,
+ default=['linux'])
+ self._menu_options['packages'] = \
+ Selector(
+ _('Additional packages'),
+ lambda preset: ask_additional_packages_to_install(preset),
+ display_func=lambda x: self.defined_text if x else '',
+ preview_func=self._prev_additional_pkgs,
+ default=[])
+ self._menu_options['additional-repositories'] = \
+ Selector(
+ _('Optional repositories'),
+ lambda preset: select_additional_repositories(preset),
+ display_func=lambda x: ', '.join(x) if x else None,
+ default=[])
+ self._menu_options['network_config'] = \
+ Selector(
+ _('Network configuration'),
+ lambda preset: ask_to_configure_network(preset),
+ display_func=lambda x: self._display_network_conf(x),
+ preview_func=self._prev_network_config,
+ default={})
+ self._menu_options['timezone'] = \
+ Selector(
+ _('Timezone'),
+ lambda preset: ask_for_a_timezone(preset),
+ default='UTC')
+ self._menu_options['ntp'] = \
+ Selector(
+ _('Automatic time sync (NTP)'),
+ lambda preset: ask_ntp(preset),
+ default=True)
+ self._menu_options['__separator__'] = \
+ Selector('')
+ self._menu_options['save_config'] = \
+ Selector(
+ _('Save configuration'),
+ lambda preset: save_config(self._data_store),
+ no_store=True)
+ self._menu_options['install'] = \
+ Selector(
+ self._install_text(),
+ exec_func=lambda n, v: self._is_config_valid(),
+ preview_func=self._prev_install_invalid_config,
+ no_store=True)
+
+ self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1))
+
+ def _missing_configs(self) -> List[str]:
+ def check(s) -> bool:
+ obj = self._menu_options.get(s)
+ if obj and obj.has_selection():
+ return True
+ return False
+
+ def has_superuser() -> bool:
+ sel = self._menu_options['!users']
+ if sel.current_selection:
+ return any([u.sudo for u in sel.current_selection])
+ return False
+
+ mandatory_fields = dict(filter(lambda x: x[1].is_mandatory(), self._menu_options.items()))
+ missing = set()
+
+ for key, selector in mandatory_fields.items():
+ if key in ['!root-password', '!users']:
+ if not check('!root-password') and not has_superuser():
+ missing.add(
+ str(_('Either root-password or at least 1 user with sudo privileges must be specified'))
+ )
+ elif key == 'disk_config':
+ if not check('disk_config'):
+ missing.add(self._menu_options['disk_config'].description)
+
+ return list(missing)
+
+ def _is_config_valid(self) -> bool:
+ """
+ Checks the validity of the current configuration.
+ """
+ if len(self._missing_configs()) != 0:
+ return False
+ return self._validate_bootloader() is None
+
+ def _update_uki_display(self, name: Optional[str] = None):
+ if bootloader := self._menu_options['bootloader'].current_selection:
+ if not SysInfo.has_uefi() or not bootloader.has_uki_support():
+ self._menu_options['uki'].set_current_selection(False)
+ self._menu_options['uki'].set_enabled(False)
+ elif name and name == 'bootloader':
+ self._menu_options['uki'].set_enabled(True)
+
+ def _update_install_text(self, name: Optional[str] = None, value: Any = None):
+ text = self._install_text()
+ self._menu_options['install'].update_description(text)
+
+ def post_callback(self, name: Optional[str] = None, value: Any = None):
+ self._update_uki_display(name)
+ self._update_install_text(name, value)
+
+ def _install_text(self):
+ missing = len(self._missing_configs())
+ if missing > 0:
+ return _('Install ({} config(s) missing)').format(missing)
+ return _('Install')
+
+ def _display_network_conf(self, config: Optional[NetworkConfiguration]) -> str:
+ if not config:
+ return str(_('Not configured, unavailable unless setup manually'))
+
+ return config.type.display_msg()
+
+ def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]:
+ disk_config: Optional[disk.DiskLayoutConfiguration] = self._menu_options['disk_config'].current_selection
+
+ if not disk_config:
+ # this should not happen as the encryption menu has the disk_config as dependency
+ raise ValueError('No disk layout specified')
+
+ if not disk.DiskEncryption.validate_enc(disk_config):
+ return None
+
+ data_store: Dict[str, Any] = {}
+ disk_encryption = disk.DiskEncryptionMenu(disk_config, data_store, preset=preset).run()
+ return disk_encryption
+
+ def _locale_selection(self, preset: LocaleConfiguration) -> LocaleConfiguration:
+ data_store: Dict[str, Any] = {}
+ locale_config = LocaleMenu(data_store, preset).run()
+ return locale_config
+
+ def _prev_locale(self) -> Optional[str]:
+ selector = self._menu_options['locale_config']
+ if selector.has_selection():
+ config: LocaleConfiguration = selector.current_selection # type: ignore
+ output = '{}: {}\n'.format(str(_('Keyboard layout')), config.kb_layout)
+ output += '{}: {}\n'.format(str(_('Locale language')), config.sys_lang)
+ output += '{}: {}'.format(str(_('Locale encoding')), config.sys_enc)
+ return output
+ return None
+
+ def _prev_network_config(self) -> Optional[str]:
+ selector: Optional[NetworkConfiguration] = self._menu_options['network_config'].current_selection
+ if selector:
+ if selector.type == NicType.MANUAL:
+ output = FormattedOutput.as_table(selector.nics)
+ return output
+ return None
+
+ def _prev_additional_pkgs(self):
+ selector = self._menu_options['packages']
+ if selector.current_selection:
+ packages: List[str] = selector.current_selection
+ return format_cols(packages, None)
+ return None
+
+ def _prev_disk_config(self) -> Optional[str]:
+ selector = self._menu_options['disk_config']
+ disk_layout_conf: Optional[disk.DiskLayoutConfiguration] = selector.current_selection
+
+ output = ''
+ if disk_layout_conf:
+ output += str(_('Configuration type: {}')).format(disk_layout_conf.config_type.display_msg())
+
+ if disk_layout_conf.lvm_config:
+ output += '\n{}: {}'.format(str(_('LVM configuration type')), disk_layout_conf.lvm_config.config_type.display_msg())
+
+ if output:
+ return output
+
+ return None
+
+ def _display_disk_config(self, current_value: Optional[disk.DiskLayoutConfiguration] = None) -> str:
+ if current_value:
+ return current_value.config_type.display_msg()
+ return ''
+
+ def _prev_disk_encryption(self) -> Optional[str]:
+ disk_config: Optional[disk.DiskLayoutConfiguration] = self._menu_options['disk_config'].current_selection
+
+ if disk_config and not disk.DiskEncryption.validate_enc(disk_config):
+ return str(_('LVM disk encryption with more than 2 partitions is currently not supported'))
+
+ encryption: Optional[disk.DiskEncryption] = self._menu_options['disk_encryption'].current_selection
+
+ if encryption:
+ enc_type = disk.EncryptionType.type_to_text(encryption.encryption_type)
+ output = str(_('Encryption type')) + f': {enc_type}\n'
+ output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n'
+
+ if encryption.partitions:
+ output += 'Partitions: {} selected'.format(len(encryption.partitions)) + '\n'
+ elif encryption.lvm_volumes:
+ output += 'LVM volumes: {} selected'.format(len(encryption.lvm_volumes)) + '\n'
+
+ if encryption.hsm_device:
+ output += f'HSM: {encryption.hsm_device.manufacturer}'
+
+ return output
+
+ return None
+
+ def _display_disk_encryption(self, current_value: Optional[disk.DiskEncryption]) -> str:
+ if current_value:
+ return disk.EncryptionType.type_to_text(current_value.encryption_type)
+ return ''
+
+ def _validate_bootloader(self) -> Optional[str]:
+ """
+ Checks the selected bootloader is valid for the selected filesystem
+ type of the boot partition.
+
+ Returns [`None`] if the bootloader is valid, otherwise returns a
+ string with the error message.
+
+ XXX: The caller is responsible for wrapping the string with the translation
+ shim if necessary.
+ """
+ bootloader = self._menu_options['bootloader'].current_selection
+ boot_partition: Optional[disk.PartitionModification] = None
+
+ if disk_config := self._menu_options['disk_config'].current_selection:
+ for layout in disk_config.device_modifications:
+ if boot_partition := layout.get_boot_partition():
+ break
+ else:
+ return "No disk layout selected"
+
+ if boot_partition is None:
+ return "Boot partition not found"
+
+ if bootloader == Bootloader.Limine:
+ if boot_partition.fs_type != disk.FilesystemType.Fat32:
+ return "Limine does not support booting from filesystems other than FAT32"
+
+ return None
+
+ def _prev_install_invalid_config(self) -> Optional[str]:
+ if missing := self._missing_configs():
+ text = str(_('Missing configurations:\n'))
+ for m in missing:
+ text += f'- {m}\n'
+ return text[:-1] # remove last new line
+
+ if error := self._validate_bootloader():
+ return str(_(f"Invalid configuration: {error}"))
+
+ return None
+
+ def _prev_users(self) -> Optional[str]:
+ selector = self._menu_options['!users']
+ users: Optional[List[User]] = selector.current_selection
+
+ if users:
+ return FormattedOutput.as_table(users)
+ return None
+
+ def _prev_profile(self) -> Optional[str]:
+ selector = self._menu_options['profile_config']
+ profile_config: Optional[ProfileConfiguration] = selector.current_selection
+
+ if profile_config and profile_config.profile:
+ output = str(_('Profiles')) + ': '
+ if profile_names := profile_config.profile.current_selection_names():
+ output += ', '.join(profile_names) + '\n'
+ else:
+ output += profile_config.profile.name + '\n'
+
+ if profile_config.gfx_driver:
+ output += str(_('Graphics driver')) + ': ' + profile_config.gfx_driver.value + '\n'
+
+ if profile_config.greeter:
+ output += str(_('Greeter')) + ': ' + profile_config.greeter.value + '\n'
+
+ return output
+
+ return None
+
+ def _set_root_password(self) -> Optional[str]:
+ prompt = str(_('Enter root password (leave blank to disable root): '))
+ password = get_password(prompt=prompt)
+ return password
+
+ def _select_disk_config(
+ self,
+ preset: Optional[disk.DiskLayoutConfiguration] = None
+ ) -> Optional[disk.DiskLayoutConfiguration]:
+ data_store: Dict[str, Any] = {}
+ disk_config = disk.DiskLayoutConfigurationMenu(preset, data_store).run()
+
+ if disk_config != preset:
+ self._menu_options['disk_encryption'].set_current_selection(None)
+
+ return disk_config
+
+ def _select_profile(self, current_profile: Optional[ProfileConfiguration]):
+ from .profile.profile_menu import ProfileMenu
+ store: Dict[str, Any] = {}
+ profile_config = ProfileMenu(store, preset=current_profile).run()
+ return profile_config
+
+ def _select_audio(
+ self,
+ current: Optional[AudioConfiguration] = None
+ ) -> Optional[AudioConfiguration]:
+ selection = ask_for_audio_selection(current)
+ return selection
+
+ def _display_audio(self, current: Optional[AudioConfiguration]) -> str:
+ if not current:
+ return Audio.no_audio_text()
+ else:
+ return current.audio.name
+
+ def _create_user_account(self, defined_users: List[User]) -> List[User]:
+ users = ask_for_additional_users(defined_users=defined_users)
+ return users
+
+ def _mirror_configuration(self, preset: Optional[MirrorConfiguration] = None) -> Optional[MirrorConfiguration]:
+ data_store: Dict[str, Any] = {}
+ mirror_configuration = MirrorMenu(data_store, preset=preset).run()
+ return mirror_configuration
+
+ def _prev_mirror_config(self) -> Optional[str]:
+ selector = self._menu_options['mirror_config']
+
+ if selector.has_selection():
+ mirror_config: MirrorConfiguration = selector.current_selection # type: ignore
+ output = ''
+ if mirror_config.regions:
+ output += '{}: {}\n\n'.format(str(_('Mirror regions')), mirror_config.regions)
+ if mirror_config.custom_mirrors:
+ table = FormattedOutput.as_table(mirror_config.custom_mirrors)
+ output += '{}\n{}'.format(str(_('Custom mirrors')), table)
+
+ return output.strip()
+
+ return None
diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py
index 9660ea95..c8001c19 100644
--- a/archinstall/lib/hardware.py
+++ b/archinstall/lib/hardware.py
@@ -1,190 +1,318 @@
import os
-import logging
-from functools import partial
+from enum import Enum
+from functools import cached_property
from pathlib import Path
-from typing import Iterator, Optional, Union
+from typing import Optional, Dict, List, TYPE_CHECKING, Any
+from .exceptions import SysCallError
from .general import SysCommand
from .networking import list_interfaces, enrich_iface_types
-from .exceptions import SysCallError
-from .output import log
-
-__packages__ = [
- "mesa",
- "xf86-video-amdgpu",
- "xf86-video-ati",
- "xf86-video-nouveau",
- "xf86-video-vmware",
- "libva-mesa-driver",
- "libva-intel-driver",
- "intel-media-driver",
- "vulkan-radeon",
- "vulkan-intel",
- "nvidia",
-]
-
-AVAILABLE_GFX_DRIVERS = {
- # Sub-dicts are layer-2 options to be selected
- # and lists are a list of packages to be installed
- "All open-source (default)": [
- "mesa",
- "xf86-video-amdgpu",
- "xf86-video-ati",
- "xf86-video-nouveau",
- "xf86-video-vmware",
- "libva-mesa-driver",
- "libva-intel-driver",
- "intel-media-driver",
- "vulkan-radeon",
- "vulkan-intel",
- ],
- "AMD / ATI (open-source)": [
- "mesa",
- "xf86-video-amdgpu",
- "xf86-video-ati",
- "libva-mesa-driver",
- "vulkan-radeon",
- ],
- "Intel (open-source)": [
- "mesa",
- "libva-intel-driver",
- "intel-media-driver",
- "vulkan-intel",
- ],
- "Nvidia (open kernel module for newer GPUs, Turing+)": ["nvidia-open"],
- "Nvidia (open-source nouveau driver)": [
- "mesa",
- "xf86-video-nouveau",
- "libva-mesa-driver"
- ],
- "Nvidia (proprietary)": ["nvidia"],
- "VMware / VirtualBox (open-source)": ["mesa", "xf86-video-vmware"],
-}
-
-CPUINFO = Path("/proc/cpuinfo")
-MEMINFO = Path("/proc/meminfo")
-
-
-def cpuinfo() -> Iterator[dict[str, str]]:
- """Yields information about the CPUs of the system."""
- cpu = {}
-
- with CPUINFO.open() as file:
- for line in file:
- if not (line := line.strip()):
- yield cpu
- cpu = {}
- continue
-
- key, value = line.split(":", maxsplit=1)
- cpu[key.strip()] = value.strip()
-
-
-def meminfo(key: Optional[str] = None) -> Union[dict[str, int], Optional[int]]:
- """Returns a dict with memory info if called with no args
- or the value of the given key of said dict.
- """
- with MEMINFO.open() as file:
- mem_info = {
- (columns := line.strip().split())[0].rstrip(':'): int(columns[1])
- for line in file
- }
-
- if key is None:
- return mem_info
-
- return mem_info.get(key)
-
-
-def has_wifi() -> bool:
- return 'WIRELESS' in enrich_iface_types(list_interfaces().values()).values()
-
-
-def has_cpu_vendor(vendor_id: str) -> bool:
- return any(cpu.get("vendor_id") == vendor_id for cpu in cpuinfo())
-
-
-has_amd_cpu = partial(has_cpu_vendor, "AuthenticAMD")
-
-
-has_intel_cpu = partial(has_cpu_vendor, "GenuineIntel")
-
-
-def has_uefi() -> bool:
- return os.path.isdir('/sys/firmware/efi')
-
-
-def graphics_devices() -> dict:
- cards = {}
- for line in SysCommand("lspci"):
- if b' VGA ' in line or b' 3D ' in line:
- _, identifier = line.split(b': ', 1)
- cards[identifier.strip().decode('UTF-8')] = line
- return cards
-
-
-def has_nvidia_graphics() -> bool:
- return any('nvidia' in x.lower() for x in graphics_devices())
-
-
-def has_amd_graphics() -> bool:
- return any('amd' in x.lower() for x in graphics_devices())
-
-
-def has_intel_graphics() -> bool:
- return any('intel' in x.lower() for x in graphics_devices())
+from .output import debug
+from .utils.util import format_cols
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class CpuVendor(Enum):
+ AuthenticAMD = 'amd'
+ GenuineIntel = 'intel'
+ _Unknown = 'unknown'
+
+ @classmethod
+ def get_vendor(cls, name: str) -> 'CpuVendor':
+ if vendor := getattr(cls, name, None):
+ return vendor
+ else:
+ debug(f"Unknown CPU vendor '{name}' detected.")
+ return cls._Unknown
+
+ def _has_microcode(self) -> bool:
+ match self:
+ case CpuVendor.AuthenticAMD | CpuVendor.GenuineIntel:
+ return True
+ case _:
+ return False
+
+ def get_ucode(self) -> Optional[Path]:
+ if self._has_microcode():
+ return Path(self.value + '-ucode.img')
+ return None
+
+
+class GfxPackage(Enum):
+ Dkms = 'dkms'
+ IntelMediaDriver = 'intel-media-driver'
+ LibvaIntelDriver = 'libva-intel-driver'
+ LibvaMesaDriver = 'libva-mesa-driver'
+ Mesa = "mesa"
+ NvidiaDkms = 'nvidia-dkms'
+ NvidiaOpen = 'nvidia-open'
+ NvidiaOpenDkms = 'nvidia-open-dkms'
+ VulkanIntel = 'vulkan-intel'
+ VulkanRadeon = 'vulkan-radeon'
+ Xf86VideoAmdgpu = "xf86-video-amdgpu"
+ Xf86VideoAti = "xf86-video-ati"
+ Xf86VideoNouveau = 'xf86-video-nouveau'
+ Xf86VideoVmware = 'xf86-video-vmware'
+ XorgServer = 'xorg-server'
+ XorgXinit = 'xorg-xinit'
+
+
+class GfxDriver(Enum):
+ AllOpenSource = 'All open-source'
+ AmdOpenSource = 'AMD / ATI (open-source)'
+ IntelOpenSource = 'Intel (open-source)'
+ NvidiaOpenKernel = 'Nvidia (open kernel module for newer GPUs, Turing+)'
+ NvidiaOpenSource = 'Nvidia (open-source nouveau driver)'
+ NvidiaProprietary = 'Nvidia (proprietary)'
+ VMOpenSource = 'VMware / VirtualBox (open-source)'
+
+ def is_nvidia(self) -> bool:
+ match self:
+ case GfxDriver.NvidiaProprietary | \
+ GfxDriver.NvidiaOpenSource | \
+ GfxDriver.NvidiaOpenKernel:
+ return True
+ case _:
+ return False
+
+ def packages_text(self) -> str:
+ text = str(_('Installed packages')) + ':\n'
+ pkg_names = [p.value for p in self.gfx_packages()]
+ text += format_cols(sorted(pkg_names))
+ return text
+
+ def gfx_packages(self) -> List[GfxPackage]:
+ packages = [GfxPackage.XorgServer, GfxPackage.XorgXinit]
+
+ match self:
+ case GfxDriver.AllOpenSource:
+ packages += [
+ GfxPackage.Mesa,
+ GfxPackage.Xf86VideoAmdgpu,
+ GfxPackage.Xf86VideoAti,
+ GfxPackage.Xf86VideoNouveau,
+ GfxPackage.Xf86VideoVmware,
+ GfxPackage.LibvaMesaDriver,
+ GfxPackage.LibvaIntelDriver,
+ GfxPackage.IntelMediaDriver,
+ GfxPackage.VulkanRadeon,
+ GfxPackage.VulkanIntel
+ ]
+ case GfxDriver.AmdOpenSource:
+ packages += [
+ GfxPackage.Mesa,
+ GfxPackage.Xf86VideoAmdgpu,
+ GfxPackage.Xf86VideoAti,
+ GfxPackage.LibvaMesaDriver,
+ GfxPackage.VulkanRadeon
+ ]
+ case GfxDriver.IntelOpenSource:
+ packages += [
+ GfxPackage.Mesa,
+ GfxPackage.LibvaIntelDriver,
+ GfxPackage.IntelMediaDriver,
+ GfxPackage.VulkanIntel
+ ]
+ case GfxDriver.NvidiaOpenKernel:
+ packages += [
+ GfxPackage.NvidiaOpen,
+ GfxPackage.Dkms,
+ GfxPackage.NvidiaOpenDkms
+ ]
+ case GfxDriver.NvidiaOpenSource:
+ packages += [
+ GfxPackage.Mesa,
+ GfxPackage.Xf86VideoNouveau,
+ GfxPackage.LibvaMesaDriver
+ ]
+ case GfxDriver.NvidiaProprietary:
+ packages += [
+ GfxPackage.NvidiaDkms,
+ GfxPackage.Dkms,
+ ]
+ case GfxDriver.VMOpenSource:
+ packages += [
+ GfxPackage.Mesa,
+ GfxPackage.Xf86VideoVmware
+ ]
+
+ return packages
+
+class _SysInfo:
+ def __init__(self):
+ pass
+
+ @cached_property
+ def cpu_info(self) -> Dict[str, str]:
+ """
+ Returns system cpu information
+ """
+ cpu_info_path = Path("/proc/cpuinfo")
+ cpu: Dict[str, str] = {}
+
+ with cpu_info_path.open() as file:
+ for line in file:
+ if line := line.strip():
+ key, value = line.split(":", maxsplit=1)
+ cpu[key.strip()] = value.strip()
+
+ return cpu
+
+ @cached_property
+ def mem_info(self) -> Dict[str, int]:
+ """
+ Returns system memory information
+ """
+ mem_info_path = Path("/proc/meminfo")
+ mem_info: Dict[str, int] = {}
+
+ with mem_info_path.open() as file:
+ for line in file:
+ key, value = line.strip().split(':')
+ num = value.split()[0]
+ mem_info[key] = int(num)
+ return mem_info
-def cpu_vendor() -> Optional[str]:
- for cpu in cpuinfo():
- return cpu.get("vendor_id")
-
- return None
-
-
-def cpu_model() -> Optional[str]:
- for cpu in cpuinfo():
- return cpu.get("model name")
-
- return None
-
-
-def sys_vendor() -> Optional[str]:
- with open(f"/sys/devices/virtual/dmi/id/sys_vendor") as vendor:
- return vendor.read().strip()
-
-
-def product_name() -> Optional[str]:
- with open(f"/sys/devices/virtual/dmi/id/product_name") as product:
- return product.read().strip()
-
-
-def mem_available() -> Optional[int]:
- return meminfo('MemAvailable')
-
-
-def mem_free() -> Optional[int]:
- return meminfo('MemFree')
-
-
-def mem_total() -> Optional[int]:
- return meminfo('MemTotal')
-
-
-def virtualization() -> Optional[str]:
- try:
- return str(SysCommand("systemd-detect-virt")).strip('\r\n')
- except SysCallError as error:
- log(f"Could not detect virtual system: {error}", level=logging.DEBUG)
-
- return None
-
-
-def is_vm() -> bool:
- try:
- return b"none" not in b"".join(SysCommand("systemd-detect-virt")).lower()
- except SysCallError as error:
- log(f"System is not running in a VM: {error}", level=logging.DEBUG)
- return None
-
-# TODO: Add more identifiers
+ def mem_info_by_key(self, key: str) -> int:
+ return self.mem_info[key]
+
+ @cached_property
+ def loaded_modules(self) -> List[str]:
+ """
+ Returns loaded kernel modules
+ """
+ modules_path = Path('/proc/modules')
+ modules: List[str] = []
+
+ with modules_path.open() as file:
+ for line in file:
+ module = line.split(maxsplit=1)[0]
+ modules.append(module)
+
+ return modules
+
+
+_sys_info = _SysInfo()
+
+
+class SysInfo:
+ @staticmethod
+ def has_wifi() -> bool:
+ ifaces = list(list_interfaces().values())
+ return 'WIRELESS' in enrich_iface_types(ifaces).values()
+
+ @staticmethod
+ def has_uefi() -> bool:
+ return os.path.isdir('/sys/firmware/efi')
+
+ @staticmethod
+ def _graphics_devices() -> Dict[str, str]:
+ cards: Dict[str, str] = {}
+ for line in SysCommand("lspci"):
+ if b' VGA ' in line or b' 3D ' in line:
+ _, identifier = line.split(b': ', 1)
+ cards[identifier.strip().decode('UTF-8')] = str(line)
+ return cards
+
+ @staticmethod
+ def has_nvidia_graphics() -> bool:
+ return any('nvidia' in x.lower() for x in SysInfo._graphics_devices())
+
+ @staticmethod
+ def has_amd_graphics() -> bool:
+ return any('amd' in x.lower() for x in SysInfo._graphics_devices())
+
+ @staticmethod
+ def has_intel_graphics() -> bool:
+ return any('intel' in x.lower() for x in SysInfo._graphics_devices())
+
+ @staticmethod
+ def cpu_vendor() -> Optional[CpuVendor]:
+ if vendor := _sys_info.cpu_info.get('vendor_id'):
+ return CpuVendor.get_vendor(vendor)
+ return None
+
+ @staticmethod
+ def cpu_model() -> Optional[str]:
+ return _sys_info.cpu_info.get('model name', None)
+
+ @staticmethod
+ def sys_vendor() -> str:
+ with open(f"/sys/devices/virtual/dmi/id/sys_vendor") as vendor:
+ return vendor.read().strip()
+
+ @staticmethod
+ def product_name() -> str:
+ with open(f"/sys/devices/virtual/dmi/id/product_name") as product:
+ return product.read().strip()
+
+ @staticmethod
+ def mem_available() -> int:
+ return _sys_info.mem_info_by_key('MemAvailable')
+
+ @staticmethod
+ def mem_free() -> int:
+ return _sys_info.mem_info_by_key('MemFree')
+
+ @staticmethod
+ def mem_total() -> int:
+ return _sys_info.mem_info_by_key('MemTotal')
+
+ @staticmethod
+ def virtualization() -> Optional[str]:
+ try:
+ return str(SysCommand("systemd-detect-virt")).strip('\r\n')
+ except SysCallError as err:
+ debug(f"Could not detect virtual system: {err}")
+
+ return None
+
+ @staticmethod
+ def is_vm() -> bool:
+ try:
+ result = SysCommand("systemd-detect-virt")
+ return b"none" not in b"".join(result).lower()
+ except SysCallError as err:
+ debug(f"System is not running in a VM: {err}")
+
+ return False
+
+ @staticmethod
+ def requires_sof_fw() -> bool:
+ return 'snd_sof' in _sys_info.loaded_modules
+
+ @staticmethod
+ def requires_alsa_fw() -> bool:
+ modules = (
+ 'snd_asihpi',
+ 'snd_cs46xx',
+ 'snd_darla20',
+ 'snd_darla24',
+ 'snd_echo3g',
+ 'snd_emu10k1',
+ 'snd_gina20',
+ 'snd_gina24',
+ 'snd_hda_codec_ca0132',
+ 'snd_hdsp',
+ 'snd_indigo',
+ 'snd_indigodj',
+ 'snd_indigodjx',
+ 'snd_indigoio',
+ 'snd_indigoiox',
+ 'snd_layla20',
+ 'snd_layla24',
+ 'snd_mia',
+ 'snd_mixart',
+ 'snd_mona',
+ 'snd_pcxhr',
+ 'snd_vx_lib'
+ )
+
+ for loaded_module in _sys_info.loaded_modules:
+ if loaded_module in modules:
+ return True
+
+ return False
diff --git a/archinstall/lib/hsm/__init__.py b/archinstall/lib/hsm/__init__.py
deleted file mode 100644
index a3f64019..00000000
--- a/archinstall/lib/hsm/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .fido import Fido2
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index f1c7b3db..8292a3be 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -1,323 +1,427 @@
-import time
-import logging
+import glob
import os
import re
-import shutil
import shlex
-import pathlib
+import shutil
import subprocess
-import glob
-from types import ModuleType
-from typing import Union, Dict, Any, List, Optional, Iterator, Mapping, TYPE_CHECKING
-from .disk import get_partitions_in_use, Partition
-from .general import SysCommand, generate_password
-from .hardware import has_uefi, is_vm, cpu_vendor
-from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout
-from .disk.helpers import findmnt
-from .mirrors import use_mirrors
-from .models.disk_encryption import DiskEncryption
-from .plugins import plugins
-from .storage import storage
-from .output import log
-from .profiles import Profile
-from .disk.partition import get_mount_fs_type
+import time
+from pathlib import Path
+from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable
+
+from . import disk
from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError
+from .general import SysCommand
+from .hardware import SysInfo
+from .locale import LocaleConfiguration
+from .locale import verify_keyboard_layout, verify_x11_keyboard_layout
+from .luks import Luks2
+from .mirrors import MirrorConfiguration
+from .models.bootloader import Bootloader
+from .models.network_configuration import Nic
from .models.users import User
-from .models.subvolume import Subvolume
-from .hsm import Fido2
+from .output import log, error, info, warn, debug
+from . import pacman
+from .pacman import Pacman
+from .plugins import plugins
+from .storage import storage
if TYPE_CHECKING:
_: Any
-
# Any package that the Installer() is responsible for (optional and the default ones)
__packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"]
# Additional packages that are installed if the user is running the Live ISO with accessibility tools enabled
__accessibility_packages__ = ["brltty", "espeakup", "alsa-utils"]
-from .pacman import run_pacman
-from .models.network_configuration import NetworkConfiguration
-
-
-class InstallationFile:
- def __init__(self, installation :'Installer', filename :str, owner :str, mode :str = "w"):
- self.installation = installation
- self.filename = filename
- self.owner = owner
- self.mode = mode
- self.fh = None
-
- def __enter__(self) -> 'InstallationFile':
- self.fh = open(self.filename, self.mode)
- return self
-
- def __exit__(self, *args :str) -> None:
- self.fh.close()
- self.installation.chown(self.owner, self.filename)
-
- def write(self, data: Union[str, bytes]) -> int:
- return self.fh.write(data)
-
- def read(self, *args) -> Union[str, bytes]:
- return self.fh.read(*args)
-
-# def poll(self, *args) -> bool:
-# return self.fh.poll(*args)
-
def accessibility_tools_in_use() -> bool:
return os.system('systemctl is-active --quiet espeakup.service') == 0
class Installer:
- """
- `Installer()` is the wrapper for most basic installation steps.
- It also wraps :py:func:`~archinstall.Installer.pacstrap` among other things.
-
- :param partition: Requires a partition as the first argument, this is
- so that the installer can mount to `mountpoint` and strap packages there.
- :type partition: class:`archinstall.Partition`
-
- :param boot_partition: There's two reasons for needing a boot partition argument,
- The first being so that `mkinitcpio` can place the `vmlinuz` kernel at the right place
- during the `pacstrap` or `linux` and the base packages for a minimal installation.
- The second being when :py:func:`~archinstall.Installer.add_bootloader` is called,
- A `boot_partition` must be known to the installer before this is called.
- :type boot_partition: class:`archinstall.Partition`
-
- :param profile: A profile to install, this is optional and can be called later manually.
- This just simplifies the process by not having to call :py:func:`~archinstall.Installer.install_profile` later on.
- :type profile: str, optional
-
- :param hostname: The given /etc/hostname for the machine.
- :type hostname: str, optional
-
- """
-
- def __init__(self, target :str, *, base_packages :Optional[List[str]] = None, kernels :Optional[List[str]] = None):
- if base_packages is None:
- base_packages = __packages__[:3]
- if kernels is None:
- self.kernels = ['linux']
- else:
- self.kernels = kernels
- self.target = target
+ def __init__(
+ self,
+ target: Path,
+ disk_config: disk.DiskLayoutConfiguration,
+ disk_encryption: Optional[disk.DiskEncryption] = None,
+ base_packages: List[str] = [],
+ kernels: Optional[List[str]] = None
+ ):
+ """
+ `Installer()` is the wrapper for most basic installation steps.
+ It also wraps :py:func:`~archinstall.Installer.pacstrap` among other things.
+ """
+ self._base_packages = base_packages or __packages__[:3]
+ self.kernels = kernels or ['linux']
+ self._disk_config = disk_config
+
+ self._disk_encryption = disk_encryption or disk.DiskEncryption(disk.EncryptionType.NoEncryption)
+ self.target: Path = target
+
self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
self.milliseconds = int(str(time.time()).split('.')[1])
+ self.helper_flags: Dict[str, Any] = {'base': False, 'bootloader': None}
- self.helper_flags = {
- 'base': False,
- 'bootloader': False
- }
-
- self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
for kernel in self.kernels:
- self.base_packages.append(kernel)
+ self._base_packages.append(kernel)
# If using accessibility tools in the live environment, append those to the packages list
if accessibility_tools_in_use():
- self.base_packages.extend(__accessibility_packages__)
+ self._base_packages.extend(__accessibility_packages__)
- self.post_base_install = []
+ self.post_base_install: List[Callable] = []
# TODO: Figure out which one of these two we'll use.. But currently we're mixing them..
storage['session'] = self
storage['installation_session'] = self
- self.MODULES = []
- self.BINARIES = []
- self.FILES = []
+ self._modules: List[str] = []
+ self._binaries: List[str] = []
+ self._files: List[str] = []
+
# systemd, sd-vconsole and sd-encrypt will be replaced by udev, keymap and encrypt
# if HSM is not used to encrypt the root volume. Check mkinitcpio() function for that override.
- self.HOOKS = ["base", "systemd", "autodetect", "keyboard", "sd-vconsole", "modconf", "block", "filesystems", "fsck"]
- self.KERNEL_PARAMS = []
- self.FSTAB_ENTRIES = []
+ self._hooks: List[str] = [
+ "base", "systemd", "autodetect", "microcode", "keyboard",
+ "sd-vconsole", "modconf", "block", "filesystems", "fsck"
+ ]
+ self._kernel_params: List[str] = []
+ self._fstab_entries: List[str] = []
self._zram_enabled = False
+ self._disable_fstrim = False
- self._disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
-
- def log(self, *args :str, level :int = logging.DEBUG, **kwargs :str):
- """
- installer.log() wraps output.log() mainly to set a default log-level for this install session.
- Any manual override can be done per log() call.
- """
- log(*args, level=level, **kwargs)
+ self.pacman = Pacman(self.target, storage['arguments'].get('silent', False))
- def __enter__(self, *args :str, **kwargs :str) -> 'Installer':
+ def __enter__(self) -> 'Installer':
return self
- def __exit__(self, *args :str, **kwargs :str) -> None:
- # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
-
- if len(args) >= 2 and args[1]:
- self.log(args[1], level=logging.ERROR, fg='red')
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if exc_type is not None:
+ error(exc_val)
self.sync_log_to_install_medium()
# We avoid printing /mnt/<log path> because that might confuse people if they note it down
# and then reboot, and a identical log file will be found in the ISO medium anyway.
- print(_("[!] A log file has been created here: {}").format(os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])))
+ print(_("[!] A log file has been created here: {}").format(
+ os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])))
print(_(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"))
- raise args[1]
+ raise exc_val
if not (missing_steps := self.post_install_check()):
- self.log('Installation completed without any errors. You may now reboot.', fg='green', level=logging.INFO)
+ log('Installation completed without any errors. You may now reboot.', fg='green')
self.sync_log_to_install_medium()
-
return True
else:
- self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=logging.WARNING)
+ warn('Some required steps were not successfully installed/configured before leaving the installer:')
+
for step in missing_steps:
- self.log(f' - {step}', fg='red', level=logging.WARNING)
+ warn(f' - {step}')
- self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=logging.WARNING)
- self.log("Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=logging.WARNING)
+ warn(f"Detailed error logs can be found at: {storage['LOG_PATH']}")
+ warn("Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues")
self.sync_log_to_install_medium()
return False
- @property
- def partitions(self) -> List[Partition]:
- return get_partitions_in_use(self.target).values()
+ def remove_mod(self, mod: str):
+ if mod in self._modules:
+ self._modules.remove(mod)
- def sync_log_to_install_medium(self) -> bool:
- # Copy over the install log (if there is one) to the install medium if
- # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to.
- if self.helper_flags.get('base-strapped', False) is True:
- if filename := storage.get('LOG_FILE', None):
- absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
+ def append_mod(self, mod: str):
+ if mod not in self._modules:
+ self._modules.append(mod)
- if not os.path.isdir(f"{self.target}/{os.path.dirname(absolute_logfile)}"):
- os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}")
+ def _verify_service_stop(self):
+ """
+ Certain services might be running that affects the system during installation.
+ One such service is "reflector.service" which updates /etc/pacman.d/mirrorlist
+ We need to wait for it before we continue since we opted in to use a custom mirror/region.
+ """
- shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}")
+ if not storage['arguments'].get('skip_ntp', False):
+ info(_('Waiting for time sync (timedatectl show) to complete.'))
- return True
+ _started_wait = time.time()
+ _notified = False
+ while True:
+ if not _notified and time.time() - _started_wait > 5:
+ _notified = True
+ warn(
+ _("Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"))
- def _create_keyfile(self,luks_handle , partition :dict, password :str):
- """ roiutine to create keyfiles, so it can be moved elsewhere
- """
- if self._disk_encryption and self._disk_encryption.generate_encryption_file(partition):
- if not (cryptkey_dir := pathlib.Path(f"{self.target}/etc/cryptsetup-keys.d")).exists():
- cryptkey_dir.mkdir(parents=True)
- # Once we store the key as ../xyzloop.key systemd-cryptsetup can automatically load this key
- # if we name the device to "xyzloop".
- if partition.get('mountpoint',None):
- encryption_key_path = f"/etc/cryptsetup-keys.d/{pathlib.Path(partition['mountpoint']).name}loop.key"
- else:
- encryption_key_path = f"/etc/cryptsetup-keys.d/{pathlib.Path(partition['device_instance'].path).name}.key"
- with open(f"{self.target}{encryption_key_path}", "w") as keyfile:
- keyfile.write(generate_password(length=512))
+ time_val = SysCommand('timedatectl show --property=NTPSynchronized --value').decode()
+ if time_val and time_val.strip() == 'yes':
+ break
+ time.sleep(1)
+ else:
+ info(
+ _('Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)'))
+
+ info('Waiting for automatic mirror selection (reflector) to complete.')
+ while self._service_state('reflector') not in ('dead', 'failed', 'exited'):
+ time.sleep(1)
+
+ # info('Waiting for pacman-init.service to complete.')
+ # while self._service_state('pacman-init') not in ('dead', 'failed', 'exited'):
+ # time.sleep(1)
- os.chmod(f"{self.target}{encryption_key_path}", 0o400)
+ info(_('Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.'))
+ # Wait for the timer to kick in
+ while self._service_started('archlinux-keyring-wkd-sync.timer') is None:
+ time.sleep(1)
- luks_handle.add_key(pathlib.Path(f"{self.target}{encryption_key_path}"), password=password)
- luks_handle.crypttab(self, encryption_key_path, options=["luks", "key-slot=1"])
+ # Wait for the service to enter a finished state
+ while self._service_state('archlinux-keyring-wkd-sync.service') not in ('dead', 'failed', 'exited'):
+ time.sleep(1)
- def _has_root(self, partition :dict) -> bool:
+ def _verify_boot_part(self):
"""
- Determine if an encrypted partition contains root in it
+ Check that mounted /boot device has at minimum size for installation
+ The reason this check is here is to catch pre-mounted device configuration and potentially
+ configured one that has not gone through any previous checks (e.g. --silence mode)
+
+ NOTE: this function should be run AFTER running the mount_ordered_layout function
"""
- if partition.get("mountpoint") is None:
- if (sub_list := partition.get("btrfs",{}).get('subvolumes',{})):
- for mountpoint in [sub_list[subvolume].get("mountpoint") if isinstance(subvolume, dict) else subvolume.mountpoint for subvolume in sub_list]:
- if mountpoint == '/':
- return True
- return False
- else:
- return False
- elif partition.get("mountpoint") == '/':
- return True
- else:
- return False
+ boot_mount = self.target / 'boot'
+ lsblk_info = disk.get_lsblk_by_mountpoint(boot_mount)
+
+ if len(lsblk_info) > 0:
+ if lsblk_info[0].size < disk.Size(200, disk.Unit.MiB, disk.SectorSize.default()):
+ raise DiskError(
+ f'The boot partition mounted at {boot_mount} is not large enough to install a boot loader. '
+ f'Please resize it to at least 200MiB and re-run the installation.'
+ )
- def mount_ordered_layout(self, layouts: Dict[str, Any]) -> None:
- from .luks import luks2
- from .disk.btrfs import setup_subvolumes, mount_subvolume
-
- # set the partitions as a list not part of a tree (which we don't need anymore (i think)
- list_part = []
- list_luks_handles = []
- for blockdevice in layouts:
- list_part.extend(layouts[blockdevice]['partitions'])
-
- # TODO: Implement a proper mount-queue system that does not depend on return values.
- mount_queue = {}
-
- # we manage the encrypted partititons
- if self._disk_encryption:
- for partition in self._disk_encryption.all_partitions:
- # open the luks device and all associate stuff
- loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}"
-
- # note that we DON'T auto_unmount (i.e. close the encrypted device so it can be used
- with (luks_handle := luks2(partition['device_instance'], loopdev, self._disk_encryption.encryption_password, auto_unmount=False)) as unlocked_device:
- if self._disk_encryption.generate_encryption_file(partition) and not self._has_root(partition):
- list_luks_handles.append([luks_handle, partition, self._disk_encryption.encryption_password])
- # this way all the requesrs will be to the dm_crypt device and not to the physical partition
- partition['device_instance'] = unlocked_device
-
- if self._has_root(partition) and self._disk_encryption.generate_encryption_file(partition) is False:
- if self._disk_encryption.hsm_device:
- Fido2.fido2_enroll(self._disk_encryption.hsm_device, partition['device_instance'], self._disk_encryption.encryption_password)
-
- btrfs_subvolumes = [entry for entry in list_part if entry.get('btrfs', {}).get('subvolumes', [])]
-
- for partition in btrfs_subvolumes:
- device_instance = partition['device_instance']
- mount_options = partition.get('filesystem', {}).get('mount_options', [])
- self.mount(device_instance, "/", options=','.join(mount_options))
- setup_subvolumes(installation=self, partition_dict=partition)
- device_instance.unmount()
-
- # We then handle any special cases, such as btrfs
- for partition in btrfs_subvolumes:
- subvolumes: List[Subvolume] = partition['btrfs']['subvolumes']
- for subvolume in sorted(subvolumes, key=lambda item: item.mountpoint):
- # We cache the mount call for later
- mount_queue[subvolume.mountpoint] = lambda sub_vol=subvolume, device=partition['device_instance']: mount_subvolume(
- installation=self,
- device=device,
- subvolume=sub_vol
- )
+ def sanity_check(self):
+ # self._verify_boot_part()
+ self._verify_service_stop()
+
+ def mount_ordered_layout(self):
+ debug('Mounting ordered layout')
+
+ luks_handlers: Dict[Any, Luks2] = {}
+
+ match self._disk_encryption.encryption_type:
+ case disk.EncryptionType.NoEncryption:
+ self._mount_lvm_layout()
+ case disk.EncryptionType.Luks:
+ luks_handlers = self._prepare_luks_partitions(self._disk_encryption.partitions)
+ case disk.EncryptionType.LvmOnLuks:
+ luks_handlers = self._prepare_luks_partitions(self._disk_encryption.partitions)
+ self._import_lvm()
+ self._mount_lvm_layout(luks_handlers)
+ case disk.EncryptionType.LuksOnLvm:
+ self._import_lvm()
+ luks_handlers = self._prepare_luks_lvm(self._disk_encryption.lvm_volumes)
+ self._mount_lvm_layout(luks_handlers)
+
+ # mount all regular partitions
+ self._mount_partition_layout(luks_handlers)
+
+ def _mount_partition_layout(self, luks_handlers: Dict[Any, Luks2]):
+ debug('Mounting partition layout')
+
+ # do not mount any PVs part of the LVM configuration
+ pvs = []
+ if self._disk_config.lvm_config:
+ pvs = self._disk_config.lvm_config.get_all_pvs()
+
+ for mod in self._disk_config.device_modifications:
+ not_pv_part_mods = list(filter(lambda x: x not in pvs, mod.partitions))
+
+ # partitions have to mounted in the right order on btrfs the mountpoint will
+ # be empty as the actual subvolumes are getting mounted instead so we'll use
+ # '/' just for sorting
+ sorted_part_mods = sorted(not_pv_part_mods, key=lambda x: x.mountpoint or Path('/'))
+
+ for part_mod in sorted_part_mods:
+ if luks_handler := luks_handlers.get(part_mod):
+ self._mount_luks_partition(part_mod, luks_handler)
+ else:
+ self._mount_partition(part_mod)
- # We mount ordinary partitions, and we sort them by the mountpoint
- for partition in sorted([entry for entry in list_part if entry.get('mountpoint', False)], key=lambda part: part['mountpoint']):
- mountpoint = partition['mountpoint']
- log(f"Mounting {mountpoint} to {self.target}{mountpoint} using {partition['device_instance']}", level=logging.INFO)
+ def _mount_lvm_layout(self, luks_handlers: Dict[Any, Luks2] = {}):
+ lvm_config = self._disk_config.lvm_config
- if partition.get('filesystem',{}).get('mount_options',[]):
- mount_options = ','.join(partition['filesystem']['mount_options'])
- mount_queue[mountpoint] = lambda instance=partition['device_instance'], target=f"{self.target}{mountpoint}", options=mount_options: instance.mount(target, options=options)
- else:
- mount_queue[mountpoint] = lambda instance=partition['device_instance'], target=f"{self.target}{mountpoint}": instance.mount(target)
+ if not lvm_config:
+ debug('No lvm config defined to be mounted')
+ return
- log(f"Using mount order: {list(sorted(mount_queue.items(), key=lambda item: item[0]))}", level=logging.DEBUG, fg="white")
+ debug('Mounting LVM layout')
- # We mount everything by sorting on the mountpoint itself.
- for mountpoint, frozen_func in sorted(mount_queue.items(), key=lambda item: item[0]):
- frozen_func()
+ for vg in lvm_config.vol_groups:
+ sorted_vol = sorted(vg.volumes, key=lambda x: x.mountpoint or Path('/'))
- time.sleep(1)
+ for vol in sorted_vol:
+ if luks_handler := luks_handlers.get(vol):
+ self._mount_luks_volume(vol, luks_handler)
+ else:
+ self._mount_lvm_vol(vol)
+
+ def _prepare_luks_partitions(
+ self,
+ partitions: List[disk.PartitionModification]
+ ) -> Dict[disk.PartitionModification, Luks2]:
+ return {
+ part_mod: disk.device_handler.unlock_luks2_dev(
+ part_mod.dev_path,
+ part_mod.mapper_name,
+ self._disk_encryption.encryption_password
+ )
+ for part_mod in partitions
+ if part_mod.mapper_name and part_mod.dev_path
+ }
- try:
- findmnt(pathlib.Path(f"{self.target}{mountpoint}"), traverse=False)
- except DiskError:
- raise DiskError(f"Target {self.target}{mountpoint} never got mounted properly (unable to get mount information using findmnt).")
+ def _import_lvm(self):
+ lvm_config = self._disk_config.lvm_config
+
+ if not lvm_config:
+ debug('No lvm config defined to be imported')
+ return
+
+ for vg in lvm_config.vol_groups:
+ disk.device_handler.lvm_import_vg(vg)
+
+ for vol in vg.volumes:
+ disk.device_handler.lvm_vol_change(vol, True)
+
+ def _prepare_luks_lvm(
+ self,
+ lvm_volumes: List[disk.LvmVolume]
+ ) -> Dict[disk.LvmVolume, Luks2]:
+ return {
+ vol: disk.device_handler.unlock_luks2_dev(
+ vol.dev_path,
+ vol.mapper_name,
+ self._disk_encryption.encryption_password
+ )
+ for vol in lvm_volumes
+ if vol.mapper_name and vol.dev_path
+ }
+
+ def _mount_partition(self, part_mod: disk.PartitionModification):
+ # it would be none if it's btrfs as the subvolumes will have the mountpoints defined
+ if part_mod.mountpoint and part_mod.dev_path:
+ target = self.target / part_mod.relative_mountpoint
+ disk.device_handler.mount(part_mod.dev_path, target, options=part_mod.mount_options)
+
+ if part_mod.fs_type == disk.FilesystemType.Btrfs and part_mod.dev_path:
+ self._mount_btrfs_subvol(
+ part_mod.dev_path,
+ part_mod.btrfs_subvols,
+ part_mod.mount_options
+ )
+
+ def _mount_lvm_vol(self, volume: disk.LvmVolume):
+ if volume.fs_type != disk.FilesystemType.Btrfs:
+ if volume.mountpoint and volume.dev_path:
+ target = self.target / volume.relative_mountpoint
+ disk.device_handler.mount(volume.dev_path, target, options=volume.mount_options)
+
+ if volume.fs_type == disk.FilesystemType.Btrfs and volume.dev_path:
+ self._mount_btrfs_subvol(volume.dev_path, volume.btrfs_subvols, volume.mount_options)
+
+ def _mount_luks_partition(self, part_mod: disk.PartitionModification, luks_handler: Luks2):
+ if part_mod.fs_type != disk.FilesystemType.Btrfs:
+ if part_mod.mountpoint and luks_handler.mapper_dev:
+ target = self.target / part_mod.relative_mountpoint
+ disk.device_handler.mount(luks_handler.mapper_dev, target, options=part_mod.mount_options)
+
+ if part_mod.fs_type == disk.FilesystemType.Btrfs and luks_handler.mapper_dev:
+ self._mount_btrfs_subvol(luks_handler.mapper_dev, part_mod.btrfs_subvols, part_mod.mount_options)
+
+ def _mount_luks_volume(self, volume: disk.LvmVolume, luks_handler: Luks2):
+ if volume.fs_type != disk.FilesystemType.Btrfs:
+ if volume.mountpoint and luks_handler.mapper_dev:
+ target = self.target / volume.relative_mountpoint
+ disk.device_handler.mount(luks_handler.mapper_dev, target, options=volume.mount_options)
+
+ if volume.fs_type == disk.FilesystemType.Btrfs and luks_handler.mapper_dev:
+ self._mount_btrfs_subvol(luks_handler.mapper_dev, volume.btrfs_subvols, volume.mount_options)
+
+ def _mount_btrfs_subvol(
+ self,
+ dev_path: Path,
+ subvolumes: List[disk.SubvolumeModification],
+ mount_options: List[str] = []
+ ):
+ for subvol in subvolumes:
+ mountpoint = self.target / subvol.relative_mountpoint
+ mount_options = mount_options + [f'subvol={subvol.name}']
+ disk.device_handler.mount(dev_path, mountpoint, options=mount_options)
+
+ def generate_key_files(self):
+ match self._disk_encryption.encryption_type:
+ case disk.EncryptionType.Luks:
+ self._generate_key_files_partitions()
+ case disk.EncryptionType.LuksOnLvm:
+ self._generate_key_file_lvm_volumes()
+ case disk.EncryptionType.LvmOnLuks:
+ # currently LvmOnLuks only supports a single
+ # partitioning layout (boot + partition)
+ # so we won't need any keyfile generation atm
+ pass
+
+ def _generate_key_files_partitions(self):
+ for part_mod in self._disk_encryption.partitions:
+ gen_enc_file = self._disk_encryption.should_generate_encryption_file(part_mod)
+
+ luks_handler = Luks2(
+ part_mod.safe_dev_path,
+ mapper_name=part_mod.mapper_name,
+ password=self._disk_encryption.encryption_password
+ )
+
+ if gen_enc_file and not part_mod.is_root():
+ debug(f'Creating key-file: {part_mod.dev_path}')
+ luks_handler.create_keyfile(self.target)
+
+ if part_mod.is_root() and not gen_enc_file:
+ if self._disk_encryption.hsm_device:
+ disk.Fido2.fido2_enroll(
+ self._disk_encryption.hsm_device,
+ part_mod.safe_dev_path,
+ self._disk_encryption.encryption_password
+ )
+
+ def _generate_key_file_lvm_volumes(self):
+ for vol in self._disk_encryption.lvm_volumes:
+ gen_enc_file = self._disk_encryption.should_generate_encryption_file(vol)
+
+ luks_handler = Luks2(
+ vol.safe_dev_path,
+ mapper_name=vol.mapper_name,
+ password=self._disk_encryption.encryption_password
+ )
+
+ if gen_enc_file and not vol.is_root():
+ info(f'Creating key-file: {vol.dev_path}')
+ luks_handler.create_keyfile(self.target)
- # once everything is mounted, we generate the key files in the correct place
- for handle in list_luks_handles:
- ppath = handle[1]['device_instance'].path
- log(f"creating key-file for {ppath}",level=logging.INFO)
- self._create_keyfile(handle[0],handle[1],handle[2])
+ if vol.is_root() and not gen_enc_file:
+ if self._disk_encryption.hsm_device:
+ disk.Fido2.fido2_enroll(
+ self._disk_encryption.hsm_device,
+ vol.safe_dev_path,
+ self._disk_encryption.encryption_password
+ )
+
+ def sync_log_to_install_medium(self) -> bool:
+ # Copy over the install log (if there is one) to the install medium if
+ # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to.
+ if self.helper_flags.get('base-strapped', False) is True:
+ if filename := storage.get('LOG_FILE', None):
+ absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
+
+ if not os.path.isdir(f"{self.target}/{os.path.dirname(absolute_logfile)}"):
+ os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}")
- def mount(self, partition :Partition, mountpoint :str, create_mountpoint :bool = True, options='') -> None:
- if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'):
- os.makedirs(f'{self.target}{mountpoint}')
+ shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}")
- partition.mount(f'{self.target}{mountpoint}', options=options)
+ return True
def add_swapfile(self, size='4G', enable_resume=True, file='/swapfile'):
if file[:1] != '/':
@@ -329,176 +433,137 @@ class Installer:
SysCommand(f'chmod 0600 {self.target}{file}')
SysCommand(f'mkswap {self.target}{file}')
- self.FSTAB_ENTRIES.append(f'{file} none swap defaults 0 0')
+ self._fstab_entries.append(f'{file} none swap defaults 0 0')
if enable_resume:
- resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode('UTF-8').strip()
- resume_offset = SysCommand(f'/usr/bin/filefrag -v {self.target}{file}').decode('UTF-8').split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip()
+ resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode()
+ resume_offset = SysCommand(
+ f'/usr/bin/filefrag -v {self.target}{file}'
+ ).decode().split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip()
- self.HOOKS.append('resume')
- self.KERNEL_PARAMS.append(f'resume=UUID={resume_uuid}')
- self.KERNEL_PARAMS.append(f'resume_offset={resume_offset}')
+ self._hooks.append('resume')
+ self._kernel_params.append(f'resume=UUID={resume_uuid}')
+ self._kernel_params.append(f'resume_offset={resume_offset}')
- def post_install_check(self, *args :str, **kwargs :str) -> List[str]:
+ def post_install_check(self, *args: str, **kwargs: str) -> List[str]:
return [step for step, flag in self.helper_flags.items() if flag is False]
- def enable_multilib_repository(self):
- # Set up a regular expression pattern of a commented line containing 'multilib' within []
- pattern = re.compile(r"^#\s*\[multilib\]$")
-
- # This is used to track if the previous line is a match, so we end up uncommenting the line after the block.
- matched = False
-
- # Read in the lines from the original file
- with open("/etc/pacman.conf", "r") as pacman_conf:
- lines = pacman_conf.readlines()
-
- # Open the file again in write mode, to replace the contents
- with open("/etc/pacman.conf", "w") as pacman_conf:
- for line in lines:
- if pattern.match(line):
- # If this is the [] block containing 'multilib', uncomment it and set the matched tracking boolean.
- pacman_conf.write(line.lstrip('#'))
- matched = True
- elif matched:
- # The previous line was a match for [.*multilib.*].
- # This means we're on a line that looks like '#Include = /etc/pacman.d/mirrorlist'
- pacman_conf.write(line.lstrip('#'))
- matched = False # Reset the state of matched to False.
- else:
- pacman_conf.write(line)
-
- def enable_testing_repositories(self, enable_multilib_testing=False):
- # Set up a regular expression pattern of a commented line containing 'testing' within []
- pattern = re.compile("^#\\[.*testing.*\\]$")
-
- # This is used to track if the previous line is a match, so we end up uncommenting the line after the block.
- matched = False
-
- # Read in the lines from the original file
- with open("/etc/pacman.conf", "r") as pacman_conf:
- lines = pacman_conf.readlines()
-
- # Open the file again in write mode, to replace the contents
- with open("/etc/pacman.conf", "w") as pacman_conf:
- for line in lines:
- if pattern.match(line) and (enable_multilib_testing or 'multilib' not in line):
- # If this is the [] block containing 'testing', uncomment it and set the matched tracking boolean.
- pacman_conf.write(line.lstrip('#'))
- matched = True
- elif matched:
- # The previous line was a match for [.*testing.*].
- # This means we're on a line that looks like '#Include = /etc/pacman.d/mirrorlist'
- pacman_conf.write(line.lstrip('#'))
- matched = False # Reset the state of matched to False.
- else:
- pacman_conf.write(line)
-
- def pacstrap(self, *packages :str, **kwargs :str) -> bool:
- if type(packages[0]) in (list, tuple):
- packages = packages[0]
-
- for plugin in plugins.values():
- if hasattr(plugin, 'on_pacstrap'):
- if (result := plugin.on_pacstrap(packages)):
- packages = result
+ def set_mirrors(self, mirror_config: MirrorConfiguration, on_target: bool = False):
+ """
+ Set the mirror configuration for the installation.
- self.log(f'Installing packages: {packages}', level=logging.INFO)
+ :param mirror_config: The mirror configuration to use.
+ :type mirror_config: MirrorConfiguration
- # TODO: We technically only need to run the -Syy once.
- try:
- run_pacman('-Syy', default_cmd='/usr/bin/pacman')
- except SysCallError as error:
- self.log(f'Could not sync a new package database: {error}', level=logging.ERROR, fg="red")
+ :on_target: Whether to set the mirrors on the target system or the live system.
+ :param on_target: bool
+ """
+ debug('Setting mirrors')
- if storage['arguments'].get('silent', False) is False:
- if input('Would you like to re-try this download? (Y/n): ').lower().strip() in ('', 'y'):
- return self.pacstrap(*packages, **kwargs)
+ for plugin in plugins.values():
+ if hasattr(plugin, 'on_mirrors'):
+ if result := plugin.on_mirrors(mirror_config):
+ mirror_config = result
- raise RequirementError(f'Could not sync mirrors: {error}', level=logging.ERROR, fg="red")
+ if on_target:
+ local_pacman_conf = Path(f'{self.target}/etc/pacman.conf')
+ local_mirrorlist_conf = Path(f'{self.target}/etc/pacman.d/mirrorlist')
+ else:
+ local_pacman_conf = Path('/etc/pacman.conf')
+ local_mirrorlist_conf = Path('/etc/pacman.d/mirrorlist')
- try:
- SysCommand(f'/usr/bin/pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm', peek_output=True)
- return True
- except SysCallError as error:
- self.log(f'Could not strap in packages: {error}', level=logging.ERROR, fg="red")
+ mirrorlist_config = mirror_config.mirrorlist_config()
+ pacman_config = mirror_config.pacman_config()
- if storage['arguments'].get('silent', False) is False:
- if input('Would you like to re-try this download? (Y/n): ').lower().strip() in ('', 'y'):
- return self.pacstrap(*packages, **kwargs)
+ if pacman_config:
+ debug(f'Pacman config: {pacman_config}')
- raise RequirementError("Pacstrap failed. See /var/log/archinstall/install.log or above message for error details.")
+ with local_pacman_conf.open('a') as fp:
+ fp.write(pacman_config)
- def set_mirrors(self, mirrors :Mapping[str, Iterator[str]]) -> None:
- for plugin in plugins.values():
- if hasattr(plugin, 'on_mirrors'):
- if result := plugin.on_mirrors(mirrors):
- mirrors = result
+ if mirrorlist_config:
+ debug(f'Mirrorlist: {mirrorlist_config}')
- return use_mirrors(mirrors, destination=f'{self.target}/etc/pacman.d/mirrorlist')
+ with local_mirrorlist_conf.open('a') as fp:
+ fp.write(mirrorlist_config)
- def genfstab(self, flags :str = '-pU') -> bool:
- self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO)
+ def genfstab(self, flags: str = '-pU'):
+ fstab_path = self.target / "etc" / "fstab"
+ info(f"Updating {fstab_path}")
try:
- fstab = SysCommand(f'/usr/bin/genfstab {flags} {self.target}')
- except SysCallError as error:
- raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {error}')
+ gen_fstab = SysCommand(f'/usr/bin/genfstab {flags} {self.target}').output()
+ except SysCallError as err:
+ raise RequirementError(
+ f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {err}')
- with open(f"{self.target}/etc/fstab", 'a') as fstab_fh:
- fstab_fh.write(fstab.decode())
+ with open(fstab_path, 'ab') as fp:
+ fp.write(gen_fstab)
- if not os.path.isfile(f'{self.target}/etc/fstab'):
- raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {fstab}')
+ if not fstab_path.is_file():
+ raise RequirementError(f'Could not create fstab file')
for plugin in plugins.values():
if hasattr(plugin, 'on_genfstab'):
if plugin.on_genfstab(self) is True:
break
- with open(f"{self.target}/etc/fstab", 'a') as fstab_fh:
- for entry in self.FSTAB_ENTRIES:
- fstab_fh.write(f'{entry}\n')
+ with open(fstab_path, 'a') as fp:
+ for entry in self._fstab_entries:
+ fp.write(f'{entry}\n')
- return True
-
- def set_hostname(self, hostname: str, *args :str, **kwargs :str) -> None:
+ def set_hostname(self, hostname: str):
with open(f'{self.target}/etc/hostname', 'w') as fh:
fh.write(hostname + '\n')
- def set_locale(self, locale :str, encoding :str = 'UTF-8', *args :str, **kwargs :str) -> bool:
- if not len(locale):
- return True
-
+ def set_locale(self, locale_config: LocaleConfiguration) -> bool:
modifier = ''
+ lang = locale_config.sys_lang
+ encoding = locale_config.sys_enc
# This is a temporary patch to fix #1200
- if '.' in locale:
- locale, potential_encoding = locale.split('.', 1)
+ if '.' in locale_config.sys_lang:
+ lang, potential_encoding = locale_config.sys_lang.split('.', 1)
# Override encoding if encoding is set to the default parameter
# and the "found" encoding differs.
- if encoding == 'UTF-8' and encoding != potential_encoding:
+ if locale_config.sys_enc == 'UTF-8' and locale_config.sys_enc != potential_encoding:
encoding = potential_encoding
# Make sure we extract the modifier, that way we can put it in if needed.
- if '@' in locale:
- locale, modifier = locale.split('@', 1)
+ if '@' in locale_config.sys_lang:
+ lang, modifier = locale_config.sys_lang.split('@', 1)
modifier = f"@{modifier}"
# - End patch
- with open(f'{self.target}/etc/locale.gen', 'a') as fh:
- fh.write(f'{locale}.{encoding}{modifier} {encoding}\n')
- with open(f'{self.target}/etc/locale.conf', 'w') as fh:
- fh.write(f'LANG={locale}.{encoding}{modifier}\n')
+ locale_gen = self.target / 'etc/locale.gen'
+ locale_gen_lines = locale_gen.read_text().splitlines(True)
+
+ # A locale entry in /etc/locale.gen may or may not contain the encoding
+ # in the first column of the entry; check for both cases.
+ entry_re = re.compile(rf'#{lang}(\.{encoding})?{modifier} {encoding}')
+
+ for index, line in enumerate(locale_gen_lines):
+ if entry_re.match(line):
+ uncommented_line = line.removeprefix('#')
+ locale_gen_lines[index] = uncommented_line
+ locale_gen.write_text(''.join(locale_gen_lines))
+ lang_value = uncommented_line.split()[0]
+ break
+ else:
+ error(f"Invalid locale: language '{locale_config.sys_lang}', encoding '{locale_config.sys_enc}'")
+ return False
try:
SysCommand(f'/usr/bin/arch-chroot {self.target} locale-gen')
- return True
- except SysCallError:
+ except SysCallError as e:
+ error(f'Failed to run locale-gen on target: {e}')
return False
- def set_timezone(self, zone :str, *args :str, **kwargs :str) -> bool:
+ (self.target / 'etc/locale.conf').write_text(f'LANG={lang_value}\n')
+ return True
+
+ def set_timezone(self, zone: str) -> bool:
if not zone:
return True
if not len(zone):
@@ -509,62 +574,49 @@ class Installer:
if result := plugin.on_timezone(zone):
zone = result
- if (pathlib.Path("/usr") / "share" / "zoneinfo" / zone).exists():
- (pathlib.Path(self.target) / "etc" / "localtime").unlink(missing_ok=True)
+ if (Path("/usr") / "share" / "zoneinfo" / zone).exists():
+ (Path(self.target) / "etc" / "localtime").unlink(missing_ok=True)
SysCommand(f'/usr/bin/arch-chroot {self.target} ln -s /usr/share/zoneinfo/{zone} /etc/localtime')
return True
else:
- self.log(
- f"Time zone {zone} does not exist, continuing with system default.",
- level=logging.WARNING,
- fg='red'
- )
+ warn(f'Time zone {zone} does not exist, continuing with system default')
return False
- def activate_ntp(self) -> None:
- log(f"activate_ntp() is deprecated, use activate_time_syncronization()", fg="yellow", level=logging.INFO)
- self.activate_time_syncronization()
-
- def activate_time_syncronization(self) -> None:
- self.log('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers.', level=logging.INFO)
+ def activate_time_synchronization(self) -> None:
+ info('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers')
self.enable_service('systemd-timesyncd')
- with open(f"{self.target}/etc/systemd/timesyncd.conf", "w") as fh:
- fh.write("[Time]\n")
- fh.write("NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org\n")
- fh.write("FallbackNTP=0.pool.ntp.org 1.pool.ntp.org 0.fr.pool.ntp.org\n")
-
- from .systemd import Boot
- with Boot(self) as session:
- session.SysCommand(["timedatectl", "set-ntp", 'true'])
-
def enable_espeakup(self) -> None:
- self.log('Enabling espeakup.service for speech synthesis (accessibility).', level=logging.INFO)
+ info('Enabling espeakup.service for speech synthesis (accessibility)')
self.enable_service('espeakup')
def enable_periodic_trim(self) -> None:
- self.log("Enabling periodic TRIM")
+ info("Enabling periodic TRIM")
# fstrim is owned by util-linux, a dependency of both base and systemd.
self.enable_service("fstrim.timer")
- def enable_service(self, *services :str) -> None:
+ def enable_service(self, services: Union[str, List[str]]) -> None:
+ if isinstance(services, str):
+ services = [services]
+
for service in services:
- self.log(f'Enabling service {service}', level=logging.INFO)
+ info(f'Enabling service {service}')
+
try:
self.arch_chroot(f'systemctl enable {service}')
- except SysCallError as error:
- raise ServiceException(f"Unable to start service {service}: {error}")
+ except SysCallError as err:
+ raise ServiceException(f"Unable to start service {service}: {err}")
for plugin in plugins.values():
if hasattr(plugin, 'on_service'):
plugin.on_service(service)
- def run_command(self, cmd :str, *args :str, **kwargs :str) -> None:
+ def run_command(self, cmd: str, *args: str, **kwargs: str) -> SysCommand:
return SysCommand(f'/usr/bin/arch-chroot {self.target} {cmd}')
- def arch_chroot(self, cmd :str, run_as :Optional[str] = None):
+ def arch_chroot(self, cmd: str, run_as: Optional[str] = None) -> SysCommand:
if run_as:
cmd = f"su - {run_as} -c {shlex.quote(cmd)}"
@@ -573,38 +625,23 @@ class Installer:
def drop_to_shell(self) -> None:
subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True)
- def configure_nic(self, network_config: NetworkConfiguration) -> None:
- from .systemd import Networkd
-
- if network_config.dhcp:
- conf = Networkd(Match={"Name": network_config.iface}, Network={"DHCP": "yes"})
- else:
- network = {"Address": network_config.ip}
- if network_config.gateway:
- network["Gateway"] = network_config.gateway
- if network_config.dns:
- dns = network_config.dns
- network["DNS"] = dns if isinstance(dns, list) else [dns]
-
- conf = Networkd(Match={"Name": network_config.iface}, Network=network)
+ def configure_nic(self, nic: Nic):
+ conf = nic.as_systemd_config()
for plugin in plugins.values():
if hasattr(plugin, 'on_configure_nic'):
- new_conf = plugin.on_configure_nic(
- network_config.iface,
- network_config.dhcp,
- network_config.ip,
- network_config.gateway,
- network_config.dns
- )
-
- if new_conf:
- conf = new_conf
-
- with open(f"{self.target}/etc/systemd/network/10-{network_config.iface}.network", "a") as netconf:
+ conf = plugin.on_configure_nic(
+ nic.iface,
+ nic.dhcp,
+ nic.ip,
+ nic.gateway,
+ nic.dns
+ ) or conf
+
+ with open(f"{self.target}/etc/systemd/network/10-{nic.iface}.network", "a") as netconf:
netconf.write(str(conf))
- def copy_iso_network_config(self, enable_services :bool = False) -> bool:
+ def copy_iso_network_config(self, enable_services: bool = False) -> bool:
# Copy (if any) iwd password and config files
if os.path.isdir('/var/lib/iwd/'):
if psk_files := glob.glob('/var/lib/iwd/*.psk'):
@@ -614,19 +651,19 @@ class Installer:
if enable_services:
# If we haven't installed the base yet (function called pre-maturely)
if self.helper_flags.get('base', False) is False:
- self.base_packages.append('iwd')
+ self._base_packages.append('iwd')
# This function will be called after minimal_installation()
# as a hook for post-installs. This hook is only needed if
# base is not installed yet.
- def post_install_enable_iwd_service(*args :str, **kwargs :str):
+ def post_install_enable_iwd_service(*args: str, **kwargs: str):
self.enable_service('iwd')
self.post_base_install.append(post_install_enable_iwd_service)
# Otherwise, we can go ahead and add the required package
# and enable it's service:
else:
- self.pacstrap('iwd')
+ self.pacman.strap('iwd')
self.enable_service('iwd')
for psk in psk_files:
@@ -644,179 +681,208 @@ class Installer:
# If we haven't installed the base yet (function called pre-maturely)
if self.helper_flags.get('base', False) is False:
- def post_install_enable_networkd_resolved(*args :str, **kwargs :str):
- self.enable_service('systemd-networkd', 'systemd-resolved')
+ def post_install_enable_networkd_resolved(*args: str, **kwargs: str):
+ self.enable_service(['systemd-networkd', 'systemd-resolved'])
self.post_base_install.append(post_install_enable_networkd_resolved)
# Otherwise, we can go ahead and enable the services
else:
- self.enable_service('systemd-networkd', 'systemd-resolved')
+ self.enable_service(['systemd-networkd', 'systemd-resolved'])
return True
- def detect_encryption(self, partition :Partition) -> bool:
- from .disk.mapperdev import MapperDev
- from .disk.dmcryptdev import DMCryptDev
- from .disk.helpers import get_filesystem_type
-
- if type(partition) is MapperDev:
- # Returns MapperDev.partition
- return partition.partition
- elif type(partition) is DMCryptDev:
- return partition.MapperDev.partition
- elif get_filesystem_type(partition.path) == 'crypto_LUKS':
- return partition
-
- return False
-
- def mkinitcpio(self, *flags :str) -> bool:
+ def mkinitcpio(self, flags: List[str]) -> bool:
for plugin in plugins.values():
if hasattr(plugin, 'on_mkinitcpio'):
# Allow plugins to override the usage of mkinitcpio altogether.
if plugin.on_mkinitcpio(self):
return True
- # mkinitcpio will error out if there's no vconsole.
- if (vconsole := pathlib.Path(f"{self.target}/etc/vconsole.conf")).exists() is False:
- with vconsole.open('w') as fh:
- fh.write(f"KEYMAP={storage['arguments']['keyboard-layout']}\n")
-
with open(f'{self.target}/etc/mkinitcpio.conf', 'w') as mkinit:
- mkinit.write(f"MODULES=({' '.join(self.MODULES)})\n")
- mkinit.write(f"BINARIES=({' '.join(self.BINARIES)})\n")
- mkinit.write(f"FILES=({' '.join(self.FILES)})\n")
+ mkinit.write(f"MODULES=({' '.join(self._modules)})\n")
+ mkinit.write(f"BINARIES=({' '.join(self._binaries)})\n")
+ mkinit.write(f"FILES=({' '.join(self._files)})\n")
- if self._disk_encryption and not self._disk_encryption.hsm_device:
+ if not self._disk_encryption.hsm_device:
# For now, if we don't use HSM we revert to the old
# way of setting up encryption hooks for mkinitcpio.
# This is purely for stability reasons, we're going away from this.
# * systemd -> udev
# * sd-vconsole -> keymap
- self.HOOKS = [hook.replace('systemd', 'udev').replace('sd-vconsole', 'keymap') for hook in self.HOOKS]
+ self._hooks = [hook.replace('systemd', 'udev').replace('sd-vconsole', 'keymap') for hook in self._hooks]
- mkinit.write(f"HOOKS=({' '.join(self.HOOKS)})\n")
+ mkinit.write(f"HOOKS=({' '.join(self._hooks)})\n")
try:
- SysCommand(f'/usr/bin/arch-chroot {self.target} mkinitcpio {" ".join(flags)}')
+ SysCommand(f'/usr/bin/arch-chroot {self.target} mkinitcpio {" ".join(flags)}', peek_output=True)
return True
- except SysCallError:
+ except SysCallError as error:
+ if error.worker:
+ log(error.worker._trace_log.decode())
return False
- def minimal_installation(
- self, testing: bool = False, multilib: bool = False,
- hostname: str = 'archinstall', locales: List[str] = ['en_US.UTF-8 UTF-8']) -> bool:
- # Add necessary packages if encrypting the drive
- # (encrypted partitions default to btrfs for now, so we need btrfs-progs)
- # TODO: Perhaps this should be living in the function which dictates
- # the partitioning. Leaving here for now.
-
- for partition in self.partitions:
- if partition.filesystem == 'btrfs':
- # if partition.encrypted:
- if 'btrfs-progs' not in self.base_packages:
- self.base_packages.append('btrfs-progs')
- if partition.filesystem == 'xfs':
- if 'xfs' not in self.base_packages:
- self.base_packages.append('xfsprogs')
- if partition.filesystem == 'f2fs':
- if 'f2fs' not in self.base_packages:
- self.base_packages.append('f2fs-tools')
-
- # Configure mkinitcpio to handle some specific use cases.
- if partition.filesystem == 'btrfs':
- if 'btrfs' not in self.MODULES:
- self.MODULES.append('btrfs')
- if '/usr/bin/btrfs' not in self.BINARIES:
- self.BINARIES.append('/usr/bin/btrfs')
- # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed.
- if partition.filesystem == 'ntfs3' and partition.mountpoint == self.target:
- if 'fsck' in self.HOOKS:
- self.HOOKS.remove('fsck')
-
- if self.detect_encryption(partition):
- if self._disk_encryption and self._disk_encryption.hsm_device:
- # Required bby mkinitcpio to add support for fido2-device options
- self.pacstrap('libfido2')
-
- if 'sd-encrypt' not in self.HOOKS:
- self.HOOKS.insert(self.HOOKS.index('filesystems'), 'sd-encrypt')
- else:
- if 'encrypt' not in self.HOOKS:
- self.HOOKS.insert(self.HOOKS.index('filesystems'), 'encrypt')
-
- if not has_uefi():
- self.base_packages.append('grub')
-
- if not is_vm():
- vendor = cpu_vendor()
- if vendor == "AuthenticAMD":
- self.base_packages.append("amd-ucode")
- if (ucode := pathlib.Path(f"{self.target}/boot/amd-ucode.img")).exists():
- ucode.unlink()
- elif vendor == "GenuineIntel":
- self.base_packages.append("intel-ucode")
- if (ucode := pathlib.Path(f"{self.target}/boot/intel-ucode.img")).exists():
- ucode.unlink()
+ def _get_microcode(self) -> Optional[Path]:
+ if not SysInfo.is_vm():
+ if vendor := SysInfo.cpu_vendor():
+ return vendor.get_ucode()
+ return None
+
+ def _handle_partition_installation(self):
+ pvs = []
+ if self._disk_config.lvm_config:
+ pvs = self._disk_config.lvm_config.get_all_pvs()
+
+ for mod in self._disk_config.device_modifications:
+ for part in mod.partitions:
+ if part in pvs or part.fs_type is None:
+ continue
+
+ if (pkg := part.fs_type.installation_pkg) is not None:
+ self._base_packages.append(pkg)
+ if (module := part.fs_type.installation_module) is not None:
+ self._modules.append(module)
+ if (binary := part.fs_type.installation_binary) is not None:
+ self._binaries.append(binary)
+
+ # https://github.com/archlinux/archinstall/issues/1837
+ if part.fs_type.fs_type_mount == 'btrfs':
+ self._disable_fstrim = True
+
+ # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed.
+ if part.fs_type.fs_type_mount == 'ntfs3' and part.mountpoint == self.target:
+ if 'fsck' in self._hooks:
+ self._hooks.remove('fsck')
+
+ if part in self._disk_encryption.partitions:
+ if self._disk_encryption.hsm_device:
+ # Required by mkinitcpio to add support for fido2-device options
+ self.pacman.strap('libfido2')
+
+ if 'sd-encrypt' not in self._hooks:
+ self._hooks.insert(self._hooks.index('filesystems'), 'sd-encrypt')
+ else:
+ if 'encrypt' not in self._hooks:
+ self._hooks.insert(self._hooks.index('filesystems'), 'encrypt')
+
+ def _handle_lvm_installation(self):
+ if not self._disk_config.lvm_config:
+ return
+
+ self.add_additional_packages('lvm2')
+ self._hooks.insert(self._hooks.index('filesystems') - 1, 'lvm2')
+
+ for vg in self._disk_config.lvm_config.vol_groups:
+ for vol in vg.volumes:
+ if vol.fs_type is not None:
+ if (pkg := vol.fs_type.installation_pkg) is not None:
+ self._base_packages.append(pkg)
+ if (module := vol.fs_type.installation_module) is not None:
+ self._modules.append(module)
+ if (binary := vol.fs_type.installation_binary) is not None:
+ self._binaries.append(binary)
+
+ if vol.fs_type.fs_type_mount == 'btrfs':
+ self._disable_fstrim = True
+
+ # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed.
+ if vol.fs_type.fs_type_mount == 'ntfs3' and vol.mountpoint == self.target:
+ if 'fsck' in self._hooks:
+ self._hooks.remove('fsck')
+
+ if self._disk_encryption.encryption_type in [disk.EncryptionType.LvmOnLuks, disk.EncryptionType.LuksOnLvm]:
+ if self._disk_encryption.hsm_device:
+ # Required by mkinitcpio to add support for fido2-device options
+ self.pacman.strap('libfido2')
+
+ if 'sd-encrypt' not in self._hooks:
+ self._hooks.insert(self._hooks.index('lvm2') - 1, 'sd-encrypt')
else:
- self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't install any ucode.", level=logging.DEBUG)
+ if 'encrypt' not in self._hooks:
+ self._hooks.insert(self._hooks.index('lvm2') - 1, 'encrypt')
+
+ def minimal_installation(
+ self,
+ testing: bool = False,
+ multilib: bool = False,
+ mkinitcpio: bool = True,
+ hostname: str = 'archinstall',
+ locale_config: LocaleConfiguration = LocaleConfiguration.default()
+ ):
+ if self._disk_config.lvm_config:
+ self._handle_lvm_installation()
+ else:
+ self._handle_partition_installation()
+
+ if not SysInfo.has_uefi():
+ self._base_packages.append('grub')
+
+ if ucode := self._get_microcode():
+ (self.target / 'boot' / ucode).unlink(missing_ok=True)
+ self._base_packages.append(ucode.stem)
+ else:
+ debug('Archinstall will not install any ucode.')
# Determine whether to enable multilib/testing repositories before running pacstrap if testing flag is set.
# This action takes place on the host system as pacstrap copies over package repository lists.
+ pacman_conf = pacman.Config(self.target)
if multilib:
- self.log("The multilib flag is set. This system will be installed with the multilib repository enabled.")
- self.enable_multilib_repository()
+ info("The multilib flag is set. This system will be installed with the multilib repository enabled.")
+ pacman_conf.enable(pacman.Repo.Multilib)
else:
- self.log("The multilib flag is not set. This system will be installed without multilib repositories enabled.")
+ info("The multilib flag is not set. This system will be installed without multilib repositories enabled.")
if testing:
- self.log("The testing flag is set. This system will be installed with testing repositories enabled.")
- self.enable_testing_repositories(multilib)
+ info("The testing flag is set. This system will be installed with testing repositories enabled.")
+ pacman_conf.enable(pacman.Repo.Testing)
else:
- self.log("The testing flag is not set. This system will be installed without testing repositories enabled.")
+ info("The testing flag is not set. This system will be installed without testing repositories enabled.")
+
+ pacman_conf.apply()
- self.pacstrap(self.base_packages)
+ self.pacman.strap(self._base_packages)
self.helper_flags['base-strapped'] = True
- # This handles making sure that the repositories we enabled persist on the installed system
- if multilib or testing:
- shutil.copy2("/etc/pacman.conf", f"{self.target}/etc/pacman.conf")
+ pacman_conf.persist()
# Periodic TRIM may improve the performance and longevity of SSDs whilst
# having no adverse effect on other devices. Most distributions enable
# periodic TRIM by default.
#
# https://github.com/archlinux/archinstall/issues/880
- self.enable_periodic_trim()
+ # https://github.com/archlinux/archinstall/issues/1837
+ # https://github.com/archlinux/archinstall/issues/1841
+ if not self._disable_fstrim:
+ self.enable_periodic_trim()
# TODO: Support locale and timezone
# os.remove(f'{self.target}/etc/localtime')
# sys_command(f'/usr/bin/arch-chroot {self.target} ln -s /usr/share/zoneinfo/{localtime} /etc/localtime')
# sys_command('/usr/bin/arch-chroot /mnt hwclock --hctosys --localtime')
self.set_hostname(hostname)
- self.set_locale(*locales[0].split())
+ self.set_locale(locale_config)
+ self.set_keyboard_language(locale_config.kb_layout)
# TODO: Use python functions for this
SysCommand(f'/usr/bin/arch-chroot {self.target} chmod 700 /root')
- self.mkinitcpio('-P')
+ if mkinitcpio and not self.mkinitcpio(['-P']):
+ error('Error generating initramfs (continuing anyway)')
self.helper_flags['base'] = True
# Run registered post-install hooks
for function in self.post_base_install:
- self.log(f"Running post-installation hook: {function}", level=logging.INFO)
+ info(f"Running post-installation hook: {function}")
function(self)
for plugin in plugins.values():
if hasattr(plugin, 'on_install'):
plugin.on_install(self)
- return True
-
- def setup_swap(self, kind :str = 'zram') -> bool:
+ def setup_swap(self, kind: str = 'zram'):
if kind == 'zram':
- self.log(f"Setting up swap on zram")
- self.pacstrap('zram-generator')
+ info(f"Setting up swap on zram")
+ self.pacman.strap('zram-generator')
# We could use the default example below, but maybe not the best idea: https://github.com/archlinux/archinstall/pull/678#issuecomment-962124813
# zram_example_location = '/usr/share/doc/zram-generator/zram-generator.conf.example'
@@ -827,224 +893,532 @@ class Installer:
self.enable_service('systemd-zram-setup@zram0.service')
self._zram_enabled = True
-
- return True
else:
raise ValueError(f"Archinstall currently only supports setting up swap on zram")
- def add_systemd_bootloader(self, boot_partition :Partition, root_partition :Partition) -> bool:
- self.pacstrap('efibootmgr')
+ def _get_efi_partition(self) -> Optional[disk.PartitionModification]:
+ for layout in self._disk_config.device_modifications:
+ if partition := layout.get_efi_partition():
+ return partition
+ return None
+
+ def _get_boot_partition(self) -> Optional[disk.PartitionModification]:
+ for layout in self._disk_config.device_modifications:
+ if boot := layout.get_boot_partition():
+ return boot
+ return None
+
+ def _get_root(self) -> Optional[disk.PartitionModification | disk.LvmVolume]:
+ if self._disk_config.lvm_config:
+ return self._disk_config.lvm_config.get_root_volume()
+ else:
+ for mod in self._disk_config.device_modifications:
+ if root := mod.get_root_partition():
+ return root
+ return None
+
+ def _get_luks_uuid_from_mapper_dev(self, mapper_dev_path: Path) -> str:
+ lsblk_info = disk.get_lsblk_info(mapper_dev_path, reverse=True, full_dev_path=True)
+
+ if not lsblk_info.children or not lsblk_info.children[0].uuid:
+ raise ValueError('Unable to determine UUID of luks superblock')
+
+ return lsblk_info.children[0].uuid
+
+ def _get_kernel_params_partition(
+ self,
+ root_partition: disk.PartitionModification,
+ id_root: bool = True,
+ partuuid: bool = True
+ ) -> List[str]:
+ kernel_parameters = []
+
+ if root_partition in self._disk_encryption.partitions:
+ # TODO: We need to detect if the encrypted device is a whole disk encryption,
+ # or simply a partition encryption. Right now we assume it's a partition (and we always have)
+
+ if self._disk_encryption and self._disk_encryption.hsm_device:
+ debug(f'Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}')
+ # Note: UUID must be used, not PARTUUID for sd-encrypt to work
+ kernel_parameters.append(f'rd.luks.name={root_partition.uuid}=root')
+ # Note: tpm2-device and fido2-device don't play along very well:
+ # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645
+ kernel_parameters.append('rd.luks.options=fido2-device=auto,password-echo=no')
+ elif partuuid:
+ debug(f'Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}')
+ kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:root')
+ else:
+ debug(f'Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}')
+ kernel_parameters.append(f'cryptdevice=UUID={root_partition.uuid}:root')
+
+ if id_root:
+ kernel_parameters.append('root=/dev/mapper/root')
+ elif id_root:
+ if partuuid:
+ debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}')
+ kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}')
+ else:
+ debug(f'Identifying root partition by UUID: {root_partition.uuid}')
+ kernel_parameters.append(f'root=UUID={root_partition.uuid}')
+
+ return kernel_parameters
+
+ def _get_kernel_params_lvm(
+ self,
+ lvm: disk.LvmVolume
+ ) -> List[str]:
+ kernel_parameters = []
+
+ match self._disk_encryption.encryption_type:
+ case disk.EncryptionType.LvmOnLuks:
+ if not lvm.vg_name:
+ raise ValueError(f'Unable to determine VG name for {lvm.name}')
+
+ pv_seg_info = disk.device_handler.lvm_pvseg_info(lvm.vg_name, lvm.name)
- if not has_uefi():
+ if not pv_seg_info:
+ raise ValueError(f'Unable to determine PV segment info for {lvm.vg_name}/{lvm.name}')
+
+ uuid = self._get_luks_uuid_from_mapper_dev(pv_seg_info.pv_name)
+
+ if self._disk_encryption.hsm_device:
+ debug(f'LvmOnLuks, encrypted root partition, HSM, identifying by UUID: {uuid}')
+ kernel_parameters.append(f'rd.luks.name={uuid}=cryptlvm root={lvm.safe_dev_path}')
+ else:
+ debug(f'LvmOnLuks, encrypted root partition, identifying by UUID: {uuid}')
+ kernel_parameters.append(f'cryptdevice=UUID={uuid}:cryptlvm root={lvm.safe_dev_path}')
+ case disk.EncryptionType.LuksOnLvm:
+ uuid = self._get_luks_uuid_from_mapper_dev(lvm.mapper_path)
+
+ if self._disk_encryption.hsm_device:
+ debug(f'LuksOnLvm, encrypted root partition, HSM, identifying by UUID: {uuid}')
+ kernel_parameters.append(f'rd.luks.name={uuid}=root root=/dev/mapper/root')
+ else:
+ debug(f'LuksOnLvm, encrypted root partition, identifying by UUID: {uuid}')
+ kernel_parameters.append(f'cryptdevice=UUID={uuid}:root root=/dev/mapper/root')
+ case disk.EncryptionType.NoEncryption:
+ debug(f'Identifying root lvm by mapper device: {lvm.dev_path}')
+ kernel_parameters.append(f'root={lvm.safe_dev_path}')
+
+ return kernel_parameters
+
+ def _get_kernel_params(
+ self,
+ root: disk.PartitionModification | disk.LvmVolume,
+ id_root: bool = True,
+ partuuid: bool = True
+ ) -> List[str]:
+ kernel_parameters = []
+
+ if isinstance(root, disk.LvmVolume):
+ kernel_parameters = self._get_kernel_params_lvm(root)
+ else:
+ kernel_parameters = self._get_kernel_params_partition(root, id_root, partuuid)
+
+ # Zswap should be disabled when using zram.
+ # https://github.com/archlinux/archinstall/issues/881
+ if self._zram_enabled:
+ kernel_parameters.append('zswap.enabled=0')
+
+ if id_root:
+ for sub_vol in root.btrfs_subvols:
+ if sub_vol.is_root():
+ kernel_parameters.append(f'rootflags=subvol={sub_vol.name}')
+ break
+
+ kernel_parameters.append('rw')
+
+ kernel_parameters.append(f'rootfstype={root.safe_fs_type.fs_type_mount}')
+ kernel_parameters.extend(self._kernel_params)
+
+ debug(f'kernel parameters: {" ".join(kernel_parameters)}')
+
+ return kernel_parameters
+
+ def _add_systemd_bootloader(
+ self,
+ boot_partition: disk.PartitionModification,
+ root: disk.PartitionModification | disk.LvmVolume,
+ efi_partition: Optional[disk.PartitionModification],
+ uki_enabled: bool = False
+ ):
+ debug('Installing systemd bootloader')
+
+ self.pacman.strap('efibootmgr')
+
+ if not SysInfo.has_uefi():
raise HardwareIncompatibilityError
+
# TODO: Ideally we would want to check if another config
# points towards the same disk and/or partition.
# And in which case we should do some clean up.
+ bootctl_options = []
+
+ if efi_partition and boot_partition != efi_partition:
+ bootctl_options.append(f'--esp-path={efi_partition.mountpoint}')
+ bootctl_options.append(f'--boot-path={boot_partition.mountpoint}')
# Install the boot loader
try:
- SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --path=/boot install')
+ SysCommand(f"/usr/bin/arch-chroot {self.target} bootctl {' '.join(bootctl_options)} install")
except SysCallError:
# Fallback, try creating the boot loader without touching the EFI variables
- SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --no-variables --path=/boot install')
+ SysCommand(f"/usr/bin/arch-chroot {self.target} bootctl --no-variables {' '.join(bootctl_options)} install")
- # Ensure that the /boot/loader directory exists before we try to create files in it
- if not os.path.exists(f'{self.target}/boot/loader'):
- os.makedirs(f'{self.target}/boot/loader')
+ # Ensure that the $BOOT/loader/ directory exists before we try to create files in it.
+ #
+ # As mentioned in https://github.com/archlinux/archinstall/pull/1859 - we store the
+ # loader entries in $BOOT/loader/ rather than $ESP/loader/
+ # The current reasoning being that $BOOT works in both use cases as well
+ # as being tied to the current installation. This may change.
+ loader_dir = self.target / 'boot/loader'
+ loader_dir.mkdir(parents=True, exist_ok=True)
+
+ default_kernel = self.kernels[0]
+ if uki_enabled:
+ default_entry = f'arch-{default_kernel}.efi'
+ else:
+ entry_name = self.init_time + '_{kernel}{variant}.conf'
+ default_entry = entry_name.format(kernel=default_kernel, variant='')
+
+ default = f'default {default_entry}'
# Modify or create a loader.conf
- if os.path.isfile(f'{self.target}/boot/loader/loader.conf'):
- with open(f'{self.target}/boot/loader/loader.conf', 'r') as loader:
- loader_data = loader.read().split('\n')
- else:
+ loader_conf = loader_dir / 'loader.conf'
+
+ try:
+ loader_data = loader_conf.read_text().splitlines()
+ except FileNotFoundError:
loader_data = [
- f"default {self.init_time}",
- "timeout 15"
+ default,
+ 'timeout 15'
]
-
- with open(f'{self.target}/boot/loader/loader.conf', 'w') as loader:
- for line in loader_data:
- if line[:8] == 'default ':
- loader.write(f'default {self.init_time}_{self.kernels[0]}\n')
- elif line[:8] == '#timeout' and 'timeout 15' not in loader_data:
+ else:
+ for index, line in enumerate(loader_data):
+ if line.startswith('default'):
+ loader_data[index] = default
+ elif line.startswith('#timeout'):
# We add in the default timeout to support dual-boot
- loader.write(f"{line[1:]}\n")
- else:
- loader.write(f"{line}\n")
+ loader_data[index] = line.removeprefix('#')
+
+ loader_conf.write_text('\n'.join(loader_data) + '\n')
- # Ensure that the /boot/loader/entries directory exists before we try to create files in it
- if not os.path.exists(f'{self.target}/boot/loader/entries'):
- os.makedirs(f'{self.target}/boot/loader/entries')
+ if uki_enabled:
+ return
+
+ # Ensure that the $BOOT/loader/entries/ directory exists before we try to create files in it
+ entries_dir = loader_dir / 'entries'
+ entries_dir.mkdir(parents=True, exist_ok=True)
+
+ comments = (
+ '# Created by: archinstall',
+ f'# Created on: {self.init_time}'
+ )
+
+ options = 'options ' + ' '.join(self._get_kernel_params(root))
for kernel in self.kernels:
for variant in ("", "-fallback"):
# Setup the loader entry
- with open(f'{self.target}/boot/loader/entries/{self.init_time}_{kernel}{variant}.conf', 'w') as entry:
- entry.write('# Created by: archinstall\n')
- entry.write(f'# Created on: {self.init_time}\n')
- entry.write(f'title Arch Linux ({kernel}{variant})\n')
- entry.write(f"linux /vmlinuz-{kernel}\n")
- if not is_vm():
- vendor = cpu_vendor()
- if vendor == "AuthenticAMD":
- entry.write("initrd /amd-ucode.img\n")
- elif vendor == "GenuineIntel":
- entry.write("initrd /intel-ucode.img\n")
- else:
- self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to systemd-boot config.", level=logging.DEBUG)
- entry.write(f"initrd /initramfs-{kernel}{variant}.img\n")
- # blkid doesn't trigger on loopback devices really well,
- # so we'll use the old manual method until we get that sorted out.
- root_fs_type = get_mount_fs_type(root_partition.filesystem)
-
- if root_fs_type is not None:
- options_entry = f'rw rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}\n'
- else:
- options_entry = f'rw {" ".join(self.KERNEL_PARAMS)}\n'
-
- for subvolume in root_partition.subvolumes:
- if subvolume.root is True and subvolume.name != '<FS_TREE>':
- options_entry = f"rootflags=subvol={subvolume.name} " + options_entry
-
- # Zswap should be disabled when using zram.
- #
- # https://github.com/archlinux/archinstall/issues/881
- if self._zram_enabled:
- options_entry = "zswap.enabled=0 " + options_entry
-
- if real_device := self.detect_encryption(root_partition):
- # TODO: We need to detect if the encrypted device is a whole disk encryption,
- # or simply a partition encryption. Right now we assume it's a partition (and we always have)
- log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.uuid}/{real_device.part_uuid}'.", level=logging.DEBUG)
-
- kernel_options = f"options"
-
- if self._disk_encryption.hsm_device:
- # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work
- kernel_options += f" rd.luks.name={real_device.uuid}=luksdev"
- # Note: tpm2-device and fido2-device don't play along very well:
- # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645
- kernel_options += f" rd.luks.options=fido2-device=auto,password-echo=no"
- else:
- kernel_options += f" cryptdevice=PARTUUID={real_device.part_uuid}:luksdev"
-
- entry.write(f'{kernel_options} root=/dev/mapper/luksdev {options_entry}')
-
- if self._disk_encryption and self._disk_encryption.hsm_device:
- # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work
- kernel_options += f" rd.luks.name={real_device.uuid}=luksdev"
- # Note: tpm2-device and fido2-device don't play along very well:
- # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645
- kernel_options += f" rd.luks.options=fido2-device=auto,password-echo=no"
- else:
- log(f"Identifying root partition by PARTUUID on {root_partition}, looking for '{root_partition.part_uuid}'.", level=logging.DEBUG)
- entry.write(f'options root=PARTUUID={root_partition.part_uuid} {options_entry}')
+ entry = [
+ *comments,
+ f'title Arch Linux ({kernel}{variant})',
+ f'linux /vmlinuz-{kernel}',
+ f'initrd /initramfs-{kernel}{variant}.img',
+ options,
+ ]
- self.helper_flags['bootloader'] = "systemd"
+ name = entry_name.format(kernel=kernel, variant=variant)
+ entry_conf = entries_dir / name
+ entry_conf.write_text('\n'.join(entry) + '\n')
- return True
+ self.helper_flags['bootloader'] = 'systemd'
- def add_grub_bootloader(self, boot_partition :Partition, root_partition :Partition) -> bool:
- self.pacstrap('grub') # no need?
+ def _add_grub_bootloader(
+ self,
+ boot_partition: disk.PartitionModification,
+ root: disk.PartitionModification | disk.LvmVolume,
+ efi_partition: Optional[disk.PartitionModification]
+ ):
+ debug('Installing grub bootloader')
- root_fs_type = get_mount_fs_type(root_partition.filesystem)
+ self.pacman.strap('grub') # no need?
- if real_device := self.detect_encryption(root_partition):
- root_uuid = SysCommand(f"blkid -s UUID -o value {real_device.path}").decode().rstrip()
- _file = "/etc/default/grub"
- add_to_CMDLINE_LINUX = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_uuid}:cryptlvm rootfstype={root_fs_type}\"/'"
- enable_CRYPTODISK = "sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/'"
+ grub_default = self.target / 'etc/default/grub'
+ config = grub_default.read_text()
- log(f"Using UUID {root_uuid} of {real_device} as encrypted root identifier.", level=logging.INFO)
- SysCommand(f"/usr/bin/arch-chroot {self.target} {add_to_CMDLINE_LINUX} {_file}")
- SysCommand(f"/usr/bin/arch-chroot {self.target} {enable_CRYPTODISK} {_file}")
- else:
- _file = "/etc/default/grub"
- add_to_CMDLINE_LINUX = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"rootfstype={root_fs_type}\"/'"
- SysCommand(f"/usr/bin/arch-chroot {self.target} {add_to_CMDLINE_LINUX} {_file}")
+ kernel_parameters = ' '.join(self._get_kernel_params(root, False, False))
+ config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{kernel_parameters}\2', config, 1)
+
+ grub_default.write_text(config)
+
+ info(f"GRUB boot partition: {boot_partition.dev_path}")
+
+ boot_dir = Path('/boot')
+
+ command = [
+ '/usr/bin/arch-chroot',
+ str(self.target),
+ 'grub-install',
+ '--debug'
+ ]
+
+ if SysInfo.has_uefi():
+ if not efi_partition:
+ raise ValueError('Could not detect efi partition')
+
+ info(f"GRUB EFI partition: {efi_partition.dev_path}")
+
+ self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead?
+
+ boot_dir_arg = []
+ if boot_partition.mountpoint and boot_partition.mountpoint != boot_dir:
+ boot_dir_arg.append(f'--boot-directory={boot_partition.mountpoint}')
+ boot_dir = boot_partition.mountpoint
+
+ add_options = [
+ '--target=x86_64-efi',
+ f'--efi-directory={efi_partition.mountpoint}',
+ *boot_dir_arg,
+ '--bootloader-id=GRUB',
+ '--removable'
+ ]
+
+ command.extend(add_options)
- log(f"GRUB uses {boot_partition.path} as the boot partition.", level=logging.INFO)
- if has_uefi():
- self.pacstrap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead?
try:
- SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True)
+ SysCommand(command, peek_output=True)
except SysCallError:
try:
- SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True)
- except SysCallError as error:
- raise DiskError(f"Could not install GRUB to {self.target}/boot: {error}")
+ SysCommand(command, peek_output=True)
+ except SysCallError as err:
+ raise DiskError(f"Could not install GRUB to {self.target}{efi_partition.mountpoint}: {err}")
else:
+ info(f"GRUB boot partition: {boot_partition.dev_path}")
+
+ parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path)
+
+ add_options = [
+ '--target=i386-pc',
+ '--recheck',
+ str(parent_dev_path)
+ ]
+
try:
- SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=i386-pc --recheck {boot_partition.parent}', peek_output=True)
- except SysCallError as error:
- raise DiskError(f"Could not install GRUB to {boot_partition.path}: {error}")
+ SysCommand(command + add_options, peek_output=True)
+ except SysCallError as err:
+ raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {err}")
try:
- SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o /boot/grub/grub.cfg')
- except SysCallError as error:
- raise DiskError(f"Could not configure GRUB: {error}")
+ SysCommand(
+ f'/usr/bin/arch-chroot {self.target} '
+ f'grub-mkconfig -o {boot_dir}/grub/grub.cfg'
+ )
+ except SysCallError as err:
+ raise DiskError(f"Could not configure GRUB: {err}")
self.helper_flags['bootloader'] = "grub"
- return True
+ def _add_limine_bootloader(
+ self,
+ boot_partition: disk.PartitionModification,
+ efi_partition: Optional[disk.PartitionModification],
+ root: disk.PartitionModification | disk.LvmVolume
+ ):
+ debug('Installing limine bootloader')
+
+ self.pacman.strap('limine')
+
+ info(f"Limine boot partition: {boot_partition.dev_path}")
+
+ limine_path = self.target / 'usr' / 'share' / 'limine'
+ hook_command = None
+
+ if SysInfo.has_uefi():
+ if not efi_partition:
+ raise ValueError('Could not detect efi partition')
+ elif not efi_partition.mountpoint:
+ raise ValueError('EFI partition is not mounted')
+
+ info(f"Limine EFI partition: {efi_partition.dev_path}")
+
+ try:
+ efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI' / 'BOOT'
+ efi_dir_path.mkdir(parents=True, exist_ok=True)
+
+ for file in ('BOOTIA32.EFI', 'BOOTX64.EFI'):
+ shutil.copy(limine_path / file, efi_dir_path)
+ except Exception as err:
+ raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}')
+
+ hook_command = f'/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_partition.mountpoint}/EFI/BOOT/' \
+ f' && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_partition.mountpoint}/EFI/BOOT/'
+ else:
+ parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path)
+
+ if unique_path := disk.device_handler.get_unique_path_for_device(parent_dev_path):
+ parent_dev_path = unique_path
+
+ try:
+ # The `limine-bios.sys` file contains stage 3 code.
+ shutil.copy(limine_path / 'limine-bios.sys', self.target / 'boot')
+
+ # `limine bios-install` deploys the stage 1 and 2 to the disk.
+ SysCommand(f'/usr/bin/arch-chroot {self.target} limine bios-install {parent_dev_path}', peek_output=True)
+ except Exception as err:
+ raise DiskError(f'Failed to install Limine on {parent_dev_path}: {err}')
+
+ hook_command = f'/usr/bin/limine bios-install {parent_dev_path}' \
+ f' && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/'
+
+ hook_contents = f'''[Trigger]
+Operation = Install
+Operation = Upgrade
+Type = Package
+Target = limine
+
+[Action]
+Description = Deploying Limine after upgrade...
+When = PostTransaction
+Exec = /bin/sh -c "{hook_command}"
+'''
+
+ hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks'
+ hooks_dir.mkdir(parents=True, exist_ok=True)
+
+ hook_path = hooks_dir / '99-limine.hook'
+ hook_path.write_text(hook_contents)
+
+ kernel_params = ' '.join(self._get_kernel_params(root))
+ config_contents = 'TIMEOUT=5\n'
+
+ for kernel in self.kernels:
+ for variant in ('', '-fallback'):
+ entry = [
+ f'PROTOCOL=linux',
+ f'KERNEL_PATH=boot:///vmlinuz-{kernel}',
+ f'MODULE_PATH=boot:///initramfs-{kernel}{variant}.img',
+ f'CMDLINE={kernel_params}',
+ ]
+
+ config_contents += f'\n:Arch Linux ({kernel}{variant})\n'
+ config_contents += '\n'.join([f' {it}' for it in entry]) + '\n'
- def add_efistub_bootloader(self, boot_partition :Partition, root_partition :Partition) -> bool:
- self.pacstrap('efibootmgr')
+ config_path = self.target / 'boot' / 'limine.cfg'
+ config_path.write_text(config_contents)
- if not has_uefi():
+ self.helper_flags['bootloader'] = "limine"
+
+ def _add_efistub_bootloader(
+ self,
+ boot_partition: disk.PartitionModification,
+ root: disk.PartitionModification | disk.LvmVolume,
+ uki_enabled: bool = False
+ ):
+ debug('Installing efistub bootloader')
+
+ self.pacman.strap('efibootmgr')
+
+ if not SysInfo.has_uefi():
raise HardwareIncompatibilityError
+
# TODO: Ideally we would want to check if another config
# points towards the same disk and/or partition.
# And in which case we should do some clean up.
- root_fs_type = get_mount_fs_type(root_partition.filesystem)
+ if not uki_enabled:
+ loader = '/vmlinuz-{kernel}'
+
+ entries = (
+ 'initrd=/initramfs-{kernel}.img',
+ *self._get_kernel_params(root)
+ )
+
+ cmdline = [' '.join(entries)]
+ else:
+ loader = '/EFI/Linux/arch-{kernel}.efi'
+ cmdline = []
+
+ parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path)
+
+ cmd_template = (
+ 'efibootmgr',
+ '--create',
+ '--disk', str(parent_dev_path),
+ '--part', str(boot_partition.partn),
+ '--label', 'Arch Linux ({kernel})',
+ '--loader', loader,
+ '--unicode', *cmdline,
+ '--verbose'
+ )
for kernel in self.kernels:
# Setup the firmware entry
+ cmd = [arg.format(kernel=kernel) for arg in cmd_template]
+ SysCommand(cmd)
+
+ self.helper_flags['bootloader'] = "efistub"
- label = f'Arch Linux ({kernel})'
- loader = f"/vmlinuz-{kernel}"
+ def _config_uki(
+ self,
+ root: disk.PartitionModification | disk.LvmVolume,
+ efi_partition: Optional[disk.PartitionModification]
+ ):
+ if not efi_partition or not efi_partition.mountpoint:
+ raise ValueError(f'Could not detect ESP at mountpoint {self.target}')
- kernel_parameters = []
+ # Set up kernel command line
+ with open(self.target / 'etc/kernel/cmdline', 'w') as cmdline:
+ kernel_parameters = self._get_kernel_params(root)
+ cmdline.write(' '.join(kernel_parameters) + '\n')
- if not is_vm():
- vendor = cpu_vendor()
- if vendor == "AuthenticAMD":
- kernel_parameters.append("initrd=\\amd-ucode.img")
- elif vendor == "GenuineIntel":
- kernel_parameters.append("initrd=\\intel-ucode.img")
- else:
- self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.", level=logging.DEBUG)
+ diff_mountpoint = None
- kernel_parameters.append(f"initrd=\\initramfs-{kernel}.img")
+ if efi_partition.mountpoint != Path('/efi'):
+ diff_mountpoint = str(efi_partition.mountpoint)
- # blkid doesn't trigger on loopback devices really well,
- # so we'll use the old manual method until we get that sorted out.
- if real_device := self.detect_encryption(root_partition):
- # TODO: We need to detect if the encrypted device is a whole disk encryption,
- # or simply a partition encryption. Right now we assume it's a partition (and we always have)
- log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.part_uuid}'.", level=logging.DEBUG)
- kernel_parameters.append(f'cryptdevice=PARTUUID={real_device.part_uuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}')
- else:
- log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.part_uuid}'.", level=logging.DEBUG)
- kernel_parameters.append(f'root=PARTUUID={root_partition.part_uuid} rw rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}')
+ image_re = re.compile('(.+_image="/([^"]+).+\n)')
+ uki_re = re.compile('#((.+_uki=")/[^/]+(.+\n))')
- SysCommand(f'efibootmgr --disk {boot_partition.path[:-1]} --part {boot_partition.path[-1]} --create --label "{label}" --loader {loader} --unicode \'{" ".join(kernel_parameters)}\' --verbose')
+ # Modify .preset files
+ for kernel in self.kernels:
+ preset = self.target / 'etc/mkinitcpio.d' / (kernel + '.preset')
+ config = preset.read_text().splitlines(True)
+
+ for index, line in enumerate(config):
+ # Avoid storing redundant image file
+ if m := image_re.match(line):
+ image = self.target / m.group(2)
+ image.unlink(missing_ok=True)
+ config[index] = '#' + m.group(1)
+ elif m := uki_re.match(line):
+ if diff_mountpoint:
+ config[index] = m.group(2) + diff_mountpoint + m.group(3)
+ else:
+ config[index] = m.group(1)
+ elif line.startswith('#default_options='):
+ config[index] = line.removeprefix('#')
- self.helper_flags['bootloader'] = "efistub"
+ preset.write_text(''.join(config))
- return True
+ # Directory for the UKIs
+ uki_dir = self.target / efi_partition.relative_mountpoint / 'EFI/Linux'
+ uki_dir.mkdir(parents=True, exist_ok=True)
- def add_bootloader(self, bootloader :str = 'systemd-bootctl') -> bool:
+ # Build the UKIs
+ if not self.mkinitcpio(['-P']):
+ error('Error generating initramfs (continuing anyway)')
+
+ def add_bootloader(self, bootloader: Bootloader, uki_enabled: bool = False):
"""
Adds a bootloader to the installation instance.
Archinstall supports one of three types:
* systemd-bootctl
* grub
+ * limine (beta)
* efistub (beta)
- :param bootloader: Can be one of the three strings
- 'systemd-bootctl', 'grub' or 'efistub' (beta)
+ :param bootloader: Type of bootloader to be added
"""
for plugin in plugins.values():
@@ -1054,61 +1428,41 @@ class Installer:
if plugin.on_add_bootloader(self):
return True
- if type(self.target) == str:
- self.target = pathlib.Path(self.target)
+ efi_partition = self._get_efi_partition()
+ boot_partition = self._get_boot_partition()
+ root = self._get_root()
- boot_partition = None
- root_partition = None
- for partition in self.partitions:
- if self.target / 'boot' in partition.mountpoints:
- boot_partition = partition
- elif self.target in partition.mountpoints:
- root_partition = partition
+ if boot_partition is None:
+ raise ValueError(f'Could not detect boot at mountpoint {self.target}')
- if boot_partition is None or root_partition is None:
- raise ValueError(f"Could not detect root ({root_partition}) or boot ({boot_partition}) in {self.target} based on: {self.partitions}")
+ if root is None:
+ raise ValueError(f'Could not detect root at mountpoint {self.target}')
- self.log(f'Adding bootloader {bootloader} to {boot_partition if boot_partition else root_partition}', level=logging.INFO)
+ info(f'Adding bootloader {bootloader.value} to {boot_partition.dev_path}')
- if bootloader == 'systemd-bootctl':
- self.add_systemd_bootloader(boot_partition, root_partition)
- elif bootloader == "grub-install":
- self.add_grub_bootloader(boot_partition, root_partition)
- elif bootloader == 'efistub':
- self.add_efistub_bootloader(boot_partition, root_partition)
- else:
- raise RequirementError(f"Unknown (or not yet implemented) bootloader requested: {bootloader}")
+ if uki_enabled:
+ self._config_uki(root, efi_partition)
- return True
-
- def add_additional_packages(self, *packages :str) -> bool:
- return self.pacstrap(*packages)
+ match bootloader:
+ case Bootloader.Systemd:
+ self._add_systemd_bootloader(boot_partition, root, efi_partition, uki_enabled)
+ case Bootloader.Grub:
+ self._add_grub_bootloader(boot_partition, root, efi_partition)
+ case Bootloader.Efistub:
+ self._add_efistub_bootloader(boot_partition, root, uki_enabled)
+ case Bootloader.Limine:
+ self._add_limine_bootloader(boot_partition, efi_partition, root)
- def install_profile(self, profile :str) -> ModuleType:
- """
- Installs a archinstall profile script (.py file).
- This profile can be either local, remote or part of the library.
+ def add_additional_packages(self, packages: Union[str, List[str]]) -> bool:
+ return self.pacman.strap(packages)
- :param profile: Can be a local path or a remote path (URL)
- :return: Returns the imported script as a module, this way
- you can access any remaining functions exposed by the profile.
- :rtype: module
- """
- storage['installation_session'] = self
-
- if type(profile) == str:
- profile = Profile(self, profile)
-
- self.log(f'Installing archinstall profile {profile}', level=logging.INFO)
- return profile.install()
-
- def enable_sudo(self, entity: str, group :bool = False):
- self.log(f'Enabling sudo permissions for {entity}.', level=logging.INFO)
+ def enable_sudo(self, entity: str, group: bool = False):
+ info(f'Enabling sudo permissions for {entity}')
sudoers_dir = f"{self.target}/etc/sudoers.d"
# Creates directory if not exists
- if not (sudoers_path := pathlib.Path(sudoers_dir)).exists():
+ if not (sudoers_path := Path(sudoers_dir)).exists():
sudoers_path.mkdir(parents=True)
# Guarantees sudoer confs directory recommended perms
os.chmod(sudoers_dir, 0o440)
@@ -1118,7 +1472,7 @@ class Installer:
# We count how many files are there already so we know which number to prefix the file with
num_of_rules_already = len(os.listdir(sudoers_dir))
- file_num_str = "{:02d}".format(num_of_rules_already) # We want 00_user1, 01_user2, etc
+ file_num_str = "{:02d}".format(num_of_rules_already) # We want 00_user1, 01_user2, etc
# Guarantees that entity str does not contain invalid characters for a linux file name:
# \ / : * ? " < > |
@@ -1130,7 +1484,7 @@ class Installer:
sudoers.write(f'{"%" if group else ""}{entity} ALL=(ALL) ALL\n')
# Guarantees sudoer conf file recommended perms
- os.chmod(pathlib.Path(rule_file_name), 0o440)
+ os.chmod(Path(rule_file_name), 0o440)
def create_users(self, users: Union[User, List[User]]):
if not isinstance(users, list):
@@ -1139,7 +1493,8 @@ class Installer:
for user in users:
self.user_create(user.username, user.password, user.groups, user.sudo)
- def user_create(self, user :str, password :Optional[str] = None, groups :Optional[List[str]] = None, sudo :bool = False) -> None:
+ def user_create(self, user: str, password: Optional[str] = None, groups: Optional[List[str]] = None,
+ sudo: bool = False) -> None:
if groups is None:
groups = []
@@ -1152,11 +1507,11 @@ class Installer:
handled_by_plugin = result
if not handled_by_plugin:
- self.log(f'Creating user {user}', level=logging.INFO)
+ info(f'Creating user {user}')
try:
SysCommand(f'/usr/bin/arch-chroot {self.target} useradd -m -G wheel {user}')
- except SysCallError as error:
- raise SystemError(f"Could not create user inside installation: {error}")
+ except SysCallError as err:
+ raise SystemError(f"Could not create user inside installation: {err}")
for plugin in plugins.values():
if hasattr(plugin, 'on_user_created'):
@@ -1173,8 +1528,8 @@ class Installer:
if sudo and self.enable_sudo(user):
self.helper_flags['user'] = True
- def user_set_pw(self, user :str, password :str) -> bool:
- self.log(f'Setting password for {user}', level=logging.INFO)
+ def user_set_pw(self, user: str, password: str) -> bool:
+ info(f'Setting password for {user}')
if user == 'root':
# This means the root account isn't locked/disabled with * in /etc/passwd
@@ -1190,8 +1545,8 @@ class Installer:
except SysCallError:
return False
- def user_set_shell(self, user :str, shell :str) -> bool:
- self.log(f'Setting shell for {user} to {shell}', level=logging.INFO)
+ def user_set_shell(self, user: str, shell: str) -> bool:
+ info(f'Setting shell for {user} to {shell}')
try:
SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"")
@@ -1199,7 +1554,7 @@ class Installer:
except SysCallError:
return False
- def chown(self, owner :str, path :str, options :List[str] = []) -> bool:
+ def chown(self, owner: str, path: str, options: List[str] = []) -> bool:
cleaned_path = path.replace('\'', '\\\'')
try:
SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c 'chown {' '.join(options)} {owner} {cleaned_path}'")
@@ -1207,53 +1562,75 @@ class Installer:
except SysCallError:
return False
- def create_file(self, filename :str, owner :Optional[str] = None) -> InstallationFile:
- return InstallationFile(self, filename, owner)
-
def set_keyboard_language(self, language: str) -> bool:
- log(f"Setting keyboard language to {language}", level=logging.INFO)
+ info(f"Setting keyboard language to {language}")
+
if len(language.strip()):
if not verify_keyboard_layout(language):
- self.log(f"Invalid keyboard language specified: {language}", fg="red", level=logging.ERROR)
+ error(f"Invalid keyboard language specified: {language}")
return False
# In accordance with https://github.com/archlinux/archinstall/issues/107#issuecomment-841701968
# Setting an empty keymap first, allows the subsequent call to set layout for both console and x11.
- from .systemd import Boot
+ from .boot import Boot
with Boot(self) as session:
os.system('/usr/bin/systemd-run --machine=archinstall --pty localectl set-keymap ""')
try:
session.SysCommand(["localectl", "set-keymap", language])
- except SysCallError as error:
- raise ServiceException(f"Unable to set locale '{language}' for console: {error}")
+ except SysCallError as err:
+ raise ServiceException(f"Unable to set locale '{language}' for console: {err}")
- self.log(f"Keyboard language for this installation is now set to: {language}")
+ info(f"Keyboard language for this installation is now set to: {language}")
else:
- self.log('Keyboard language was not changed from default (no language specified).', fg="yellow", level=logging.INFO)
+ info('Keyboard language was not changed from default (no language specified)')
return True
def set_x11_keyboard_language(self, language: str) -> bool:
- log(f"Setting x11 keyboard language to {language}", level=logging.INFO)
"""
A fallback function to set x11 layout specifically and separately from console layout.
This isn't strictly necessary since .set_keyboard_language() does this as well.
"""
+ info(f"Setting x11 keyboard language to {language}")
+
if len(language.strip()):
if not verify_x11_keyboard_layout(language):
- self.log(f"Invalid x11-keyboard language specified: {language}", fg="red", level=logging.ERROR)
+ error(f"Invalid x11-keyboard language specified: {language}")
return False
- from .systemd import Boot
+ from .boot import Boot
with Boot(self) as session:
session.SysCommand(["localectl", "set-x11-keymap", '""'])
try:
session.SysCommand(["localectl", "set-x11-keymap", language])
- except SysCallError as error:
- raise ServiceException(f"Unable to set locale '{language}' for X11: {error}")
+ except SysCallError as err:
+ raise ServiceException(f"Unable to set locale '{language}' for X11: {err}")
else:
- self.log(f'X11-Keyboard language was not changed from default (no language specified).', fg="yellow", level=logging.INFO)
+ info(f'X11-Keyboard language was not changed from default (no language specified)')
return True
+
+ def _service_started(self, service_name: str) -> Optional[str]:
+ if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'):
+ service_name += '.service' # Just to be safe
+
+ last_execution_time = SysCommand(
+ f"systemctl show --property=ActiveEnterTimestamp --no-pager {service_name}",
+ environment_vars={'SYSTEMD_COLORS': '0'}
+ ).decode().lstrip('ActiveEnterTimestamp=')
+
+ if not last_execution_time:
+ return None
+
+ return last_execution_time
+
+ def _service_state(self, service_name: str) -> str:
+ if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'):
+ service_name += '.service' # Just to be safe
+
+ return SysCommand(
+ f'systemctl show --no-pager -p SubState --value {service_name}',
+ environment_vars={'SYSTEMD_COLORS': '0'}
+ ).decode()
diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py
new file mode 100644
index 00000000..4b696a78
--- /dev/null
+++ b/archinstall/lib/interactions/__init__.py
@@ -0,0 +1,19 @@
+from .manage_users_conf import UserList, ask_for_additional_users
+from .network_menu import ManualNetworkConfig, ask_to_configure_network
+from .utils import get_password
+
+from .disk_conf import (
+ select_devices, select_disk_config, get_default_partition_layout,
+ select_main_filesystem_format, suggest_single_disk_layout,
+ suggest_multi_disk_layout
+)
+
+from .general_conf import (
+ ask_ntp, ask_hostname, ask_for_a_timezone, ask_for_audio_selection,
+ select_archinstall_language, ask_additional_packages_to_install,
+ add_number_of_parallel_downloads, select_additional_repositories
+)
+
+from .system_conf import (
+ select_kernel, ask_for_bootloader, ask_for_uki, select_driver, ask_for_swap
+)
diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py
new file mode 100644
index 00000000..4fce4fe5
--- /dev/null
+++ b/archinstall/lib/interactions/disk_conf.py
@@ -0,0 +1,572 @@
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Any, TYPE_CHECKING
+from typing import Optional, List, Tuple
+
+from .. import disk
+from ..disk.device_model import BtrfsMountOption
+from ..hardware import SysInfo
+from ..menu import Menu
+from ..menu import TableMenu
+from ..menu.menu import MenuSelectionType
+from ..output import FormattedOutput, debug
+from ..utils.util import prompt_dir
+from ..storage import storage
+
+if TYPE_CHECKING:
+ _: Any
+
+
+def select_devices(preset: List[disk.BDevice] = []) -> List[disk.BDevice]:
+ """
+ Asks the user to select one or multiple devices
+
+ :return: List of selected devices
+ :rtype: list
+ """
+
+ def _preview_device_selection(selection: disk._DeviceInfo) -> Optional[str]:
+ dev = disk.device_handler.get_device(selection.path)
+ if dev and dev.partition_infos:
+ return FormattedOutput.as_table(dev.partition_infos)
+ return None
+
+ if preset is None:
+ preset = []
+
+ title = str(_('Select one or more devices to use and configure'))
+ warning = str(_('If you reset the device selection this will also reset the current disk layout. Are you sure?'))
+
+ devices = disk.device_handler.devices
+ options = [d.device_info for d in devices]
+ preset_value = [p.device_info for p in preset]
+
+ choice = TableMenu(
+ title,
+ data=options,
+ multi=True,
+ preset=preset_value,
+ preview_command=_preview_device_selection,
+ preview_title=str(_('Existing Partitions')),
+ preview_size=0.2,
+ allow_reset=True,
+ allow_reset_warning_msg=warning
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset: return []
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection:
+ selected_device_info: List[disk._DeviceInfo] = choice.single_value
+ selected_devices = []
+
+ for device in devices:
+ if device.device_info in selected_device_info:
+ selected_devices.append(device)
+
+ return selected_devices
+
+
+def get_default_partition_layout(
+ devices: List[disk.BDevice],
+ filesystem_type: Optional[disk.FilesystemType] = None,
+ advanced_option: bool = False
+) -> List[disk.DeviceModification]:
+ if len(devices) == 1:
+ device_modification = suggest_single_disk_layout(
+ devices[0],
+ filesystem_type=filesystem_type,
+ advanced_options=advanced_option
+ )
+ return [device_modification]
+ else:
+ return suggest_multi_disk_layout(
+ devices,
+ filesystem_type=filesystem_type,
+ advanced_options=advanced_option
+ )
+
+
+def _manual_partitioning(
+ preset: List[disk.DeviceModification],
+ devices: List[disk.BDevice]
+) -> List[disk.DeviceModification]:
+ modifications = []
+ for device in devices:
+ mod = next(filter(lambda x: x.device == device, preset), None)
+ if not mod:
+ mod = disk.DeviceModification(device, wipe=False)
+
+ if partitions := disk.manual_partitioning(device, preset=mod.partitions):
+ mod.partitions = partitions
+ modifications.append(mod)
+
+ return modifications
+
+
+def select_disk_config(
+ preset: Optional[disk.DiskLayoutConfiguration] = None,
+ advanced_option: bool = False
+) -> Optional[disk.DiskLayoutConfiguration]:
+ default_layout = disk.DiskLayoutType.Default.display_msg()
+ manual_mode = disk.DiskLayoutType.Manual.display_msg()
+ pre_mount_mode = disk.DiskLayoutType.Pre_mount.display_msg()
+
+ options = [default_layout, manual_mode, pre_mount_mode]
+ preset_value = preset.config_type.display_msg() if preset else None
+ warning = str(_('Are you sure you want to reset this setting?'))
+
+ choice = Menu(
+ _('Select a partitioning option'),
+ options,
+ allow_reset=True,
+ allow_reset_warning_msg=warning,
+ sort=False,
+ preview_size=0.2,
+ preset_values=preset_value
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Reset: return None
+ case MenuSelectionType.Selection:
+ if choice.single_value == pre_mount_mode:
+ output = 'You will use whatever drive-setup is mounted at the specified directory\n'
+ output += "WARNING: Archinstall won't check the suitability of this setup\n"
+
+ try:
+ path = prompt_dir(str(_('Enter the root directory of the mounted devices: ')), output)
+ except (KeyboardInterrupt, EOFError):
+ return preset
+ mods = disk.device_handler.detect_pre_mounted_mods(path)
+
+ storage['MOUNT_POINT'] = Path(path)
+
+ return disk.DiskLayoutConfiguration(
+ config_type=disk.DiskLayoutType.Pre_mount,
+ device_modifications=mods,
+ mountpoint=path
+ )
+
+ preset_devices = [mod.device for mod in preset.device_modifications] if preset else []
+ devices = select_devices(preset_devices)
+
+ if not devices:
+ return None
+
+ if choice.value == default_layout:
+ modifications = get_default_partition_layout(devices, advanced_option=advanced_option)
+ if modifications:
+ return disk.DiskLayoutConfiguration(
+ config_type=disk.DiskLayoutType.Default,
+ device_modifications=modifications
+ )
+ elif choice.value == manual_mode:
+ preset_mods = preset.device_modifications if preset else []
+ modifications = _manual_partitioning(preset_mods, devices)
+
+ if modifications:
+ return disk.DiskLayoutConfiguration(
+ config_type=disk.DiskLayoutType.Manual,
+ device_modifications=modifications
+ )
+
+ return None
+
+
+def select_lvm_config(
+ disk_config: disk.DiskLayoutConfiguration,
+ preset: Optional[disk.LvmConfiguration] = None,
+) -> Optional[disk.LvmConfiguration]:
+ default_mode = disk.LvmLayoutType.Default.display_msg()
+
+ options = [default_mode]
+
+ preset_value = preset.config_type.display_msg() if preset else None
+ warning = str(_('Are you sure you want to reset this setting?'))
+
+ choice = Menu(
+ _('Select a LVM option'),
+ options,
+ allow_reset=True,
+ allow_reset_warning_msg=warning,
+ sort=False,
+ preview_size=0.2,
+ preset_values=preset_value
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Reset: return None
+ case MenuSelectionType.Selection:
+ if choice.single_value == default_mode:
+ return suggest_lvm_layout(disk_config)
+ return preset
+
+
+def _boot_partition(sector_size: disk.SectorSize, using_gpt: bool) -> disk.PartitionModification:
+ flags = [disk.PartitionFlag.Boot]
+ if using_gpt:
+ start = disk.Size(1, disk.Unit.MiB, sector_size)
+ size = disk.Size(1, disk.Unit.GiB, sector_size)
+ flags.append(disk.PartitionFlag.ESP)
+ else:
+ start = disk.Size(3, disk.Unit.MiB, sector_size)
+ size = disk.Size(203, disk.Unit.MiB, sector_size)
+
+ # boot partition
+ return disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=start,
+ length=size,
+ mountpoint=Path('/boot'),
+ fs_type=disk.FilesystemType.Fat32,
+ flags=flags
+ )
+
+
+def select_main_filesystem_format(advanced_options: bool = False) -> disk.FilesystemType:
+ options = {
+ 'btrfs': disk.FilesystemType.Btrfs,
+ 'ext4': disk.FilesystemType.Ext4,
+ 'xfs': disk.FilesystemType.Xfs,
+ 'f2fs': disk.FilesystemType.F2fs
+ }
+
+ if advanced_options:
+ options.update({'ntfs': disk.FilesystemType.Ntfs})
+
+ prompt = _('Select which filesystem your main partition should use')
+ choice = Menu(prompt, options, skip=False, sort=False).run()
+ return options[choice.single_value]
+
+
+def select_mount_options() -> List[str]:
+ prompt = str(_('Would you like to use compression or disable CoW?'))
+ options = [str(_('Use compression')), str(_('Disable Copy-on-Write'))]
+ choice = Menu(prompt, options, sort=False).run()
+
+ if choice.type_ == MenuSelectionType.Selection:
+ if choice.single_value == options[0]:
+ return [BtrfsMountOption.compress.value]
+ else:
+ return [BtrfsMountOption.nodatacow.value]
+
+ return []
+
+
+def process_root_partition_size(available_space: disk.Size, sector_size: disk.SectorSize) -> disk.Size:
+ # root partition size processing
+ total_device_size = available_space.convert(disk.Unit.GiB)
+ if total_device_size.value > 500:
+ # maximum size
+ return disk.Size(value=50, unit=disk.Unit.GiB, sector_size=sector_size)
+ elif total_device_size.value < 200:
+ # minimum size
+ return disk.Size(value=20, unit=disk.Unit.GiB, sector_size=sector_size)
+ else:
+ # 10% of total size
+ length = total_device_size.value // 10
+ return disk.Size(value=length, unit=disk.Unit.GiB, sector_size=sector_size)
+
+
+def suggest_single_disk_layout(
+ device: disk.BDevice,
+ filesystem_type: Optional[disk.FilesystemType] = None,
+ advanced_options: bool = False,
+ separate_home: Optional[bool] = None
+) -> disk.DeviceModification:
+ if not filesystem_type:
+ filesystem_type = select_main_filesystem_format(advanced_options)
+
+ sector_size = device.device_info.sector_size
+ total_size = device.device_info.total_size
+ min_size_to_allow_home_part = disk.Size(40, disk.Unit.GiB, sector_size)
+ root_partition_size = process_root_partition_size(available_space=total_size, sector_size=sector_size)
+ using_subvolumes = False
+ using_home_partition = False
+ mount_options = []
+ device_size_gib = device.device_info.total_size
+
+ if filesystem_type == disk.FilesystemType.Btrfs:
+ prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?'))
+ choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
+ using_subvolumes = choice.value == Menu.yes()
+ mount_options = select_mount_options()
+
+ device_modification = disk.DeviceModification(device, wipe=True)
+
+ using_gpt = SysInfo.has_uefi()
+
+ # Used for reference: https://wiki.archlinux.org/title/partitioning
+ # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for other bootloaders?
+
+ # TODO: On BIOS, /boot partition is only needed if the drive will
+ # be encrypted, otherwise it is not recommended. We should probably
+ # add a check for whether the drive will be encrypted or not.
+
+ # Increase the UEFI partition if UEFI is detected.
+ # Also re-align the start to 1MiB since we don't need the first sectors
+ # like we do in MBR layouts where the boot loader is installed traditionally.
+
+ boot_partition = _boot_partition(sector_size, using_gpt)
+ device_modification.add_partition(boot_partition)
+
+ if not using_subvolumes:
+ if device_size_gib >= min_size_to_allow_home_part:
+ if separate_home is None:
+ prompt = str(_('Would you like to create a separate partition for /home?'))
+ choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
+ using_home_partition = choice.value == Menu.yes()
+ elif separate_home is True:
+ using_home_partition = True
+ else:
+ using_home_partition = False
+
+ align_buffer = disk.Size(1, disk.Unit.MiB, sector_size)
+
+ # root partition
+ root_start = boot_partition.start + boot_partition.length
+
+ # Set a size for / (/root)
+ if using_subvolumes or device_size_gib < min_size_to_allow_home_part or not using_home_partition:
+ root_length = total_size - root_start
+ else:
+ root_length = min(total_size, root_partition_size)
+
+ if using_gpt and not using_home_partition:
+ root_length -= align_buffer
+
+ root_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=root_start,
+ length=root_length,
+ mountpoint=Path('/') if not using_subvolumes else None,
+ fs_type=filesystem_type,
+ mount_options=mount_options
+ )
+
+ device_modification.add_partition(root_partition)
+
+ if using_subvolumes:
+ # https://btrfs.wiki.kernel.org/index.php/FAQ
+ # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash
+ # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh
+ subvolumes = [
+ disk.SubvolumeModification(Path('@'), Path('/')),
+ disk.SubvolumeModification(Path('@home'), Path('/home')),
+ disk.SubvolumeModification(Path('@log'), Path('/var/log')),
+ disk.SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')),
+ disk.SubvolumeModification(Path('@.snapshots'), Path('/.snapshots'))
+ ]
+ root_partition.btrfs_subvols = subvolumes
+ elif using_home_partition:
+ # If we don't want to use subvolumes,
+ # But we want to be able to reuse data between re-installs..
+ # A second partition for /home would be nice if we have the space for it
+ home_start = root_partition.start + root_partition.length
+ home_length = total_size - home_start
+
+ if using_gpt:
+ home_length -= align_buffer
+
+ home_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=home_start,
+ length=home_length,
+ mountpoint=Path('/home'),
+ fs_type=filesystem_type,
+ mount_options=mount_options
+ )
+ device_modification.add_partition(home_partition)
+
+ return device_modification
+
+
+def suggest_multi_disk_layout(
+ devices: List[disk.BDevice],
+ filesystem_type: Optional[disk.FilesystemType] = None,
+ advanced_options: bool = False
+) -> List[disk.DeviceModification]:
+ if not devices:
+ return []
+
+ # Not really a rock solid foundation of information to stand on, but it's a start:
+ # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/
+ # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/
+ min_home_partition_size = disk.Size(40, disk.Unit.GiB, disk.SectorSize.default())
+ # rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size?
+ desired_root_partition_size = disk.Size(20, disk.Unit.GiB, disk.SectorSize.default())
+ mount_options = []
+
+ if not filesystem_type:
+ filesystem_type = select_main_filesystem_format(advanced_options)
+
+ # find proper disk for /home
+ possible_devices = list(filter(lambda x: x.device_info.total_size >= min_home_partition_size, devices))
+ home_device = max(possible_devices, key=lambda d: d.device_info.total_size) if possible_devices else None
+
+ # find proper device for /root
+ devices_delta = {}
+ for device in devices:
+ if device is not home_device:
+ delta = device.device_info.total_size - desired_root_partition_size
+ devices_delta[device] = delta
+
+ sorted_delta: List[Tuple[disk.BDevice, Any]] = sorted(devices_delta.items(), key=lambda x: x[1])
+ root_device: Optional[disk.BDevice] = sorted_delta[0][0]
+
+ if home_device is None or root_device is None:
+ text = _('The selected drives do not have the minimum capacity required for an automatic suggestion\n')
+ text += _('Minimum capacity for /home partition: {}GiB\n').format(min_home_partition_size.format_size(disk.Unit.GiB))
+ text += _('Minimum capacity for Arch Linux partition: {}GiB').format(desired_root_partition_size.format_size(disk.Unit.GiB))
+ Menu(str(text), [str(_('Continue'))], skip=False).run()
+ return []
+
+ if filesystem_type == disk.FilesystemType.Btrfs:
+ mount_options = select_mount_options()
+
+ device_paths = ', '.join([str(d.device_info.path) for d in devices])
+
+ debug(f'Suggesting multi-disk-layout for devices: {device_paths}')
+ debug(f'/root: {root_device.device_info.path}')
+ debug(f'/home: {home_device.device_info.path}')
+
+ root_device_modification = disk.DeviceModification(root_device, wipe=True)
+ home_device_modification = disk.DeviceModification(home_device, wipe=True)
+
+ root_device_sector_size = root_device_modification.device.device_info.sector_size
+ home_device_sector_size = home_device_modification.device.device_info.sector_size
+
+ root_align_buffer = disk.Size(1, disk.Unit.MiB, root_device_sector_size)
+ home_align_buffer = disk.Size(1, disk.Unit.MiB, home_device_sector_size)
+
+ using_gpt = SysInfo.has_uefi()
+
+ # add boot partition to the root device
+ boot_partition = _boot_partition(root_device_sector_size, using_gpt)
+ root_device_modification.add_partition(boot_partition)
+
+ root_start = boot_partition.start + boot_partition.length
+ root_length = root_device.device_info.total_size - root_start
+
+ if using_gpt:
+ root_length -= root_align_buffer
+
+ # add root partition to the root device
+ root_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=root_start,
+ length=root_length,
+ mountpoint=Path('/'),
+ mount_options=mount_options,
+ fs_type=filesystem_type
+ )
+ root_device_modification.add_partition(root_partition)
+
+ home_start = home_align_buffer
+ home_length = home_device.device_info.total_size - home_start
+
+ if using_gpt:
+ home_length -= home_align_buffer
+
+ # add home partition to home device
+ home_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=home_start,
+ length=home_length,
+ mountpoint=Path('/home'),
+ mount_options=mount_options,
+ fs_type=filesystem_type,
+ )
+ home_device_modification.add_partition(home_partition)
+
+ return [root_device_modification, home_device_modification]
+
+
+def suggest_lvm_layout(
+ disk_config: disk.DiskLayoutConfiguration,
+ filesystem_type: Optional[disk.FilesystemType] = None,
+ vg_grp_name: str = 'ArchinstallVg',
+) -> disk.LvmConfiguration:
+ if disk_config.config_type != disk.DiskLayoutType.Default:
+ raise ValueError('LVM suggested volumes are only available for default partitioning')
+
+ using_subvolumes = False
+ btrfs_subvols = []
+ home_volume = True
+ mount_options = []
+
+ if not filesystem_type:
+ filesystem_type = select_main_filesystem_format()
+
+ if filesystem_type == disk.FilesystemType.Btrfs:
+ prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?'))
+ choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run()
+ using_subvolumes = choice.value == Menu.yes()
+
+ mount_options = select_mount_options()
+
+ if using_subvolumes:
+ btrfs_subvols = [
+ disk.SubvolumeModification(Path('@'), Path('/')),
+ disk.SubvolumeModification(Path('@home'), Path('/home')),
+ disk.SubvolumeModification(Path('@log'), Path('/var/log')),
+ disk.SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')),
+ disk.SubvolumeModification(Path('@.snapshots'), Path('/.snapshots')),
+ ]
+
+ home_volume = False
+
+ boot_part: Optional[disk.PartitionModification] = None
+ other_part: List[disk.PartitionModification] = []
+
+ for mod in disk_config.device_modifications:
+ for part in mod.partitions:
+ if part.is_boot():
+ boot_part = part
+ else:
+ other_part.append(part)
+
+ if not boot_part:
+ raise ValueError('Unable to find boot partition in partition modifications')
+
+ total_vol_available = sum(
+ [p.length for p in other_part],
+ disk.Size(0, disk.Unit.B, disk.SectorSize.default()),
+ )
+ root_vol_size = disk.Size(20, disk.Unit.GiB, disk.SectorSize.default())
+ home_vol_size = total_vol_available - root_vol_size
+
+ lvm_vol_group = disk.LvmVolumeGroup(vg_grp_name, pvs=other_part, )
+
+ root_vol = disk.LvmVolume(
+ status=disk.LvmVolumeStatus.Create,
+ name='root',
+ fs_type=filesystem_type,
+ length=root_vol_size,
+ mountpoint=Path('/'),
+ btrfs_subvols=btrfs_subvols,
+ mount_options=mount_options
+ )
+
+ lvm_vol_group.volumes.append(root_vol)
+
+ if home_volume:
+ home_vol = disk.LvmVolume(
+ status=disk.LvmVolumeStatus.Create,
+ name='home',
+ fs_type=filesystem_type,
+ length=home_vol_size,
+ mountpoint=Path('/home'),
+ )
+
+ lvm_vol_group.volumes.append(home_vol)
+
+ return disk.LvmConfiguration(disk.LvmLayoutType.Default, [lvm_vol_group])
diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py
new file mode 100644
index 00000000..a879552e
--- /dev/null
+++ b/archinstall/lib/interactions/general_conf.py
@@ -0,0 +1,209 @@
+from __future__ import annotations
+
+import pathlib
+from typing import List, Any, Optional, TYPE_CHECKING
+
+from ..locale import list_timezones
+from ..menu import MenuSelectionType, Menu, TextInput
+from ..models.audio_configuration import Audio, AudioConfiguration
+from ..output import warn
+from ..packages.packages import validate_package_list
+from ..storage import storage
+from ..translationhandler import Language
+
+if TYPE_CHECKING:
+ _: Any
+
+
+def ask_ntp(preset: bool = True) -> bool:
+ prompt = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n'))
+ prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor more information, please check the Arch wiki'))
+ if preset:
+ preset_val = Menu.yes()
+ else:
+ preset_val = Menu.no()
+ choice = Menu(prompt, Menu.yes_no(), skip=False, preset_values=preset_val, default_option=Menu.yes()).run()
+
+ return False if choice.value == Menu.no() else True
+
+
+def ask_hostname(preset: str = '') -> str:
+ hostname = TextInput(
+ str(_('Desired hostname for the installation: ')),
+ preset
+ ).run().strip()
+
+ if not hostname:
+ return preset
+
+ return hostname
+
+
+def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]:
+ timezones = list_timezones()
+ default = 'UTC'
+
+ choice = Menu(
+ _('Select a timezone'),
+ timezones,
+ preset_values=preset,
+ default_option=default
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return choice.single_value
+
+ return None
+
+
+def ask_for_audio_selection(
+ current: Optional[AudioConfiguration] = None
+) -> Optional[AudioConfiguration]:
+ choices = [
+ Audio.Pipewire.name,
+ Audio.Pulseaudio.name,
+ Audio.no_audio_text()
+ ]
+
+ preset = current.audio.name if current else None
+
+ choice = Menu(
+ _('Choose an audio server'),
+ choices,
+ preset_values=preset
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return current
+ case MenuSelectionType.Selection:
+ value = choice.single_value
+ if value == Audio.no_audio_text():
+ return None
+ else:
+ return AudioConfiguration(Audio[value])
+
+ return None
+
+
+def select_language(preset: Optional[str] = None) -> Optional[str]:
+ from ..locale.locale_menu import select_kb_layout
+
+ # We'll raise an exception in an upcoming version.
+ # from ..exceptions import Deprecated
+ # raise Deprecated("select_language() has been deprecated, use select_kb_layout() instead.")
+
+ # No need to translate this i feel, as it's a short lived message.
+ warn("select_language() is deprecated, use select_kb_layout() instead. select_language() will be removed in a future version")
+
+ return select_kb_layout(preset)
+
+
+def select_archinstall_language(languages: List[Language], preset: Language) -> Language:
+ # these are the displayed language names which can either be
+ # the english name of a language or, if present, the
+ # name of the language in its own language
+ options = {lang.display_name: lang for lang in languages}
+
+ title = 'NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n'
+ title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n'
+ title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n'
+
+ choice = Menu(
+ title,
+ list(options.keys()),
+ default_option=preset.display_name,
+ preview_size=0.5
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return options[choice.single_value]
+
+ raise ValueError('Language selection not handled')
+
+
+def ask_additional_packages_to_install(preset: List[str] = []) -> List[str]:
+ # Additional packages (with some light weight error handling for invalid package names)
+ print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.'))
+ print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.'))
+
+ def read_packages(p: List = []) -> list:
+ display = ' '.join(p)
+ input_packages = TextInput(_('Write additional packages to install (space separated, leave blank to skip): '), display).run().strip()
+ return input_packages.split() if input_packages else []
+
+ preset = preset if preset else []
+ packages = read_packages(preset)
+
+ if not storage['arguments']['offline'] and not storage['arguments']['no_pkg_lookups']:
+ while True:
+ if len(packages):
+ # Verify packages that were given
+ print(_("Verifying that additional packages exist (this might take a few seconds)"))
+ valid, invalid = validate_package_list(packages)
+
+ if invalid:
+ warn(f"Some packages could not be found in the repository: {invalid}")
+ packages = read_packages(valid)
+ continue
+ break
+
+ return packages
+
+
+def add_number_of_parallel_downloads(input_number :Optional[int] = None) -> Optional[int]:
+ max_recommended = 5
+ print(_(f"This option enables the number of parallel downloads that can occur during package downloads"))
+ print(_("Enter the number of parallel downloads to be enabled.\n\nNote:\n"))
+ print(str(_(" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )")).format(max_recommended, max_recommended))
+ print(_(" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"))
+
+ while True:
+ try:
+ input_number = int(TextInput(_("[Default value: 0] > ")).run().strip() or 0)
+ if input_number <= 0:
+ input_number = 0
+ break
+ except:
+ print(str(_("Invalid input! Try again with a valid input [or 0 to disable]")).format(max_recommended))
+
+ pacman_conf_path = pathlib.Path("/etc/pacman.conf")
+ with pacman_conf_path.open() as f:
+ pacman_conf = f.read().split("\n")
+
+ with pacman_conf_path.open("w") as fwrite:
+ for line in pacman_conf:
+ if "ParallelDownloads" in line:
+ fwrite.write(f"ParallelDownloads = {input_number}\n") if not input_number == 0 else fwrite.write("#ParallelDownloads = 0\n")
+ else:
+ fwrite.write(f"{line}\n")
+
+ return input_number
+
+
+def select_additional_repositories(preset: List[str]) -> List[str]:
+ """
+ Allows the user to select additional repositories (multilib, and testing) if desired.
+
+ :return: The string as a selected repository
+ :rtype: string
+ """
+
+ repositories = ["multilib", "testing"]
+
+ choice = Menu(
+ _('Choose which optional additional repositories to enable'),
+ repositories,
+ sort=False,
+ multi=True,
+ preset_values=preset,
+ allow_reset=True
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Reset: return []
+ case MenuSelectionType.Selection: return choice.single_value
+
+ return [] \ No newline at end of file
diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/interactions/manage_users_conf.py
index 84ce3556..886f85b6 100644
--- a/archinstall/lib/user_interaction/manage_users_conf.py
+++ b/archinstall/lib/interactions/manage_users_conf.py
@@ -1,13 +1,11 @@
from __future__ import annotations
import re
-from typing import Any, Dict, TYPE_CHECKING, List, Optional
+from typing import Any, TYPE_CHECKING, List, Optional
from .utils import get_password
-from ..menu import Menu
-from ..menu.list_manager import ListManager
+from ..menu import Menu, ListManager
from ..models.users import User
-from ..output import FormattedOutput
if TYPE_CHECKING:
_: Any
@@ -27,21 +25,6 @@ class UserList(ListManager):
]
super().__init__(prompt, lusers, [self._actions[0]], self._actions[1:])
- def reformat(self, data: List[User]) -> Dict[str, User]:
- table = FormattedOutput.as_table(data)
- rows = table.split('\n')
-
- # these are the header rows of the table and do not map to any User obviously
- # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
- # the selectable rows so the header has to be aligned
- display_data = {f' {rows[0]}': None, f' {rows[1]}': None}
-
- for row, user in zip(rows[2:], data):
- row = row.replace('|', '\\|')
- display_data[row] = user
-
- return display_data
-
def selected_action_display(self, user: User) -> str:
return user.username
@@ -53,16 +36,16 @@ class UserList(ListManager):
# was created we'll replace the existing one
data = [d for d in data if d.username != new_user.username]
data += [new_user]
- elif action == self._actions[1]: # change password
+ elif action == self._actions[1] and entry: # change password
prompt = str(_('Password for user "{}": ').format(entry.username))
new_password = get_password(prompt=prompt)
if new_password:
user = next(filter(lambda x: x == entry, data))
user.password = new_password
- elif action == self._actions[2]: # promote/demote
+ elif action == self._actions[2] and entry: # promote/demote
user = next(filter(lambda x: x == entry, data))
user.sudo = False if user.sudo else True
- elif action == self._actions[3]: # delete
+ elif action == self._actions[3] and entry: # delete
data = [d for d in data if d != entry]
return data
@@ -76,20 +59,28 @@ class UserList(ListManager):
prompt = '\n\n' + str(_('Enter username (leave blank to skip): '))
while True:
- username = input(prompt).strip(' ')
+ try:
+ username = input(prompt).strip(' ')
+ except (KeyboardInterrupt, EOFError):
+ return None
+
if not username:
return None
if not self._check_for_correct_username(username):
- prompt = str(_("The username you entered is invalid. Try again")) + '\n' + prompt
+ error_prompt = str(_("The username you entered is invalid. Try again"))
+ print(error_prompt)
else:
break
password = get_password(prompt=str(_('Password for user "{}": ').format(username)))
+ if not password:
+ return None
+
choice = Menu(
str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(),
skip=False,
- default_option=Menu.no(),
+ default_option=Menu.yes(),
clear_screen=False,
show_search_hint=False
).run()
diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/interactions/network_menu.py
index 5e637f23..14fc5785 100644
--- a/archinstall/lib/user_interaction/network_conf.py
+++ b/archinstall/lib/interactions/network_menu.py
@@ -1,17 +1,14 @@
from __future__ import annotations
import ipaddress
-import logging
-from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict
+from typing import Any, Optional, TYPE_CHECKING, List, Dict
-from ..menu.menu import MenuSelectionType
-from ..menu.text_input import TextInput
-from ..models.network_configuration import NetworkConfiguration, NicType
+from ..menu import MenuSelectionType, TextInput
+from ..models.network_configuration import NetworkConfiguration, NicType, Nic
from ..networking import list_interfaces
-from ..menu import Menu
-from ..output import log, FormattedOutput
-from ..menu.list_manager import ListManager
+from ..output import FormattedOutput, warn
+from ..menu import ListManager, Menu
if TYPE_CHECKING:
_: Any
@@ -22,23 +19,22 @@ class ManualNetworkConfig(ListManager):
subclass of ListManager for the managing of network configurations
"""
- def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]):
+ def __init__(self, prompt: str, preset: List[Nic]):
self._actions = [
str(_('Add interface')),
str(_('Edit interface')),
str(_('Delete interface'))
]
+ super().__init__(prompt, preset, [self._actions[0]], self._actions[1:])
- super().__init__(prompt, ifaces, [self._actions[0]], self._actions[1:])
-
- def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]:
+ def reformat(self, data: List[Nic]) -> Dict[str, Optional[Nic]]:
table = FormattedOutput.as_table(data)
rows = table.split('\n')
# these are the header rows of the table and do not map to any User obviously
# we're adding 2 spaces as prefix because the menu selector '> ' will be put before
# the selectable rows so the header has to be aligned
- display_data: Dict[str, Optional[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None}
+ display_data: Dict[str, Optional[Nic]] = {f' {rows[0]}': None, f' {rows[1]}': None}
for row, iface in zip(rows[2:], data):
row = row.replace('|', '\\|')
@@ -46,16 +42,16 @@ class ManualNetworkConfig(ListManager):
return display_data
- def selected_action_display(self, iface: NetworkConfiguration) -> str:
- return iface.iface if iface.iface else ''
+ def selected_action_display(self, nic: Nic) -> str:
+ return nic.iface if nic.iface else ''
- def handle_action(self, action: str, entry: Optional[NetworkConfiguration], data: List[NetworkConfiguration]):
+ def handle_action(self, action: str, entry: Optional[Nic], data: List[Nic]):
if action == self._actions[0]: # add
- iface_name = self._select_iface(data)
- if iface_name:
- iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name)
- iface = self._edit_iface(iface)
- data += [iface]
+ iface = self._select_iface(data)
+ if iface:
+ nic = Nic(iface=iface)
+ nic = self._edit_iface(nic)
+ data += [nic]
elif entry:
if action == self._actions[1]: # edit interface
data = [d for d in data if d.iface != entry.iface]
@@ -65,7 +61,7 @@ class ManualNetworkConfig(ListManager):
return data
- def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]:
+ def _select_iface(self, data: List[Nic]) -> Optional[str]:
all_ifaces = list_interfaces().values()
existing_ifaces = [d.iface for d in data]
available = set(all_ifaces) - set(existing_ifaces)
@@ -74,10 +70,10 @@ class ManualNetworkConfig(ListManager):
if choice.type_ == MenuSelectionType.Skip:
return None
- return choice.value
+ return choice.single_value
- def _edit_iface(self, edit_iface: NetworkConfiguration):
- iface_name = edit_iface.iface
+ def _edit_iface(self, edit_nic: Nic) -> Nic:
+ iface_name = edit_nic.iface
modes = ['DHCP (auto detect)', 'IP (static)']
default_mode = 'DHCP (auto detect)'
@@ -87,13 +83,13 @@ class ManualNetworkConfig(ListManager):
if mode.value == 'IP (static)':
while 1:
prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name)
- ip = TextInput(prompt, edit_iface.ip).run().strip()
+ ip = TextInput(prompt, edit_nic.ip).run().strip()
# Implemented new check for correct IP/subnet input
try:
ipaddress.ip_interface(ip)
break
except ValueError:
- log("You need to enter a valid IP in IP-config mode.", level=logging.WARNING, fg='red')
+ warn("You need to enter a valid IP in IP-config mode")
# Implemented new check for correct gateway IP address
gateway = None
@@ -101,17 +97,17 @@ class ManualNetworkConfig(ListManager):
while 1:
gateway = TextInput(
_('Enter your gateway (router) IP address or leave blank for none: '),
- edit_iface.gateway
+ edit_nic.gateway
).run().strip()
try:
if len(gateway) > 0:
ipaddress.ip_address(gateway)
break
except ValueError:
- log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red')
+ warn("You need to enter a valid gateway (router) IP address")
- if edit_iface.dns:
- display_dns = ' '.join(edit_iface.dns)
+ if edit_nic.dns:
+ display_dns = ' '.join(edit_nic.dns)
else:
display_dns = None
dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip()
@@ -120,39 +116,24 @@ class ManualNetworkConfig(ListManager):
if len(dns_input):
dns = dns_input.split(' ')
- return NetworkConfiguration(NicType.MANUAL, iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False)
+ return Nic(iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False)
else:
# this will contain network iface names
- return NetworkConfiguration(NicType.MANUAL, iface=iface_name)
+ return Nic(iface=iface_name)
-def ask_to_configure_network(
- preset: Union[NetworkConfiguration, List[NetworkConfiguration]]
-) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]:
+def ask_to_configure_network(preset: Optional[NetworkConfiguration]) -> Optional[NetworkConfiguration]:
"""
Configure the network on the newly installed system
"""
- network_options = {
- 'none': str(_('No network configuration')),
- 'iso_config': str(_('Copy ISO network configuration to installation')),
- 'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')),
- 'manual': str(_('Manual configuration'))
- }
- # for this routine it's easier to set the cursor position rather than a preset value
- cursor_idx = None
-
- if preset and not isinstance(preset, list):
- if preset.type == 'iso_config':
- cursor_idx = 0
- elif preset.type == 'network_manager':
- cursor_idx = 1
-
+ options = {n.display_msg(): n for n in NicType}
+ preset_val = preset.type.display_msg() if preset else None
warning = str(_('Are you sure you want to reset this setting?'))
choice = Menu(
_('Select one network interface to configure'),
- list(network_options.values()),
- cursor_index=cursor_idx,
+ list(options.keys()),
+ preset_values=preset_val,
sort=False,
allow_reset=True,
allow_reset_warning_msg=warning
@@ -161,15 +142,18 @@ def ask_to_configure_network(
match choice.type_:
case MenuSelectionType.Skip: return preset
case MenuSelectionType.Reset: return None
-
- if choice.value == network_options['none']:
- return None
- elif choice.value == network_options['iso_config']:
- return NetworkConfiguration(NicType.ISO)
- elif choice.value == network_options['network_manager']:
- return NetworkConfiguration(NicType.NM)
- elif choice.value == network_options['manual']:
- preset_ifaces = preset if isinstance(preset, list) else []
- return ManualNetworkConfig('Configure interfaces', preset_ifaces).run()
+ case MenuSelectionType.Selection:
+ nic_type = options[choice.single_value]
+
+ match nic_type:
+ case NicType.ISO:
+ return NetworkConfiguration(NicType.ISO)
+ case NicType.NM:
+ return NetworkConfiguration(NicType.NM)
+ case NicType.MANUAL:
+ preset_nics = preset.nics if preset else []
+ nics = ManualNetworkConfig('Configure interfaces', preset_nics).run()
+ if nics:
+ return NetworkConfiguration(NicType.MANUAL, nics)
return preset
diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py
new file mode 100644
index 00000000..35ba5a8b
--- /dev/null
+++ b/archinstall/lib/interactions/system_conf.py
@@ -0,0 +1,138 @@
+from __future__ import annotations
+
+from typing import List, Any, TYPE_CHECKING, Optional
+
+from ..hardware import SysInfo, GfxDriver
+from ..menu import MenuSelectionType, Menu
+from ..models.bootloader import Bootloader
+
+if TYPE_CHECKING:
+ _: Any
+
+
+def select_kernel(preset: List[str] = []) -> List[str]:
+ """
+ Asks the user to select a kernel for system.
+
+ :return: The string as a selected kernel
+ :rtype: string
+ """
+
+ kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"]
+ default_kernel = "linux"
+
+ warning = str(_('Are you sure you want to reset this setting?'))
+
+ choice = Menu(
+ _('Choose which kernels to use or leave blank for default "{}"').format(default_kernel),
+ kernels,
+ sort=True,
+ multi=True,
+ preset_values=preset,
+ allow_reset_warning_msg=warning
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return choice.single_value
+
+ return []
+
+
+def ask_for_bootloader(preset: Bootloader) -> Bootloader:
+ # Systemd is UEFI only
+ if not SysInfo.has_uefi():
+ options = [Bootloader.Grub.value, Bootloader.Limine.value]
+ default = Bootloader.Grub.value
+ else:
+ options = Bootloader.values()
+ default = Bootloader.Systemd.value
+
+ preset_value = preset.value if preset else None
+
+ choice = Menu(
+ _('Choose a bootloader'),
+ options,
+ preset_values=preset_value,
+ sort=False,
+ default_option=default
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return Bootloader(choice.value)
+
+ return preset
+
+
+def ask_for_uki(preset: bool = True) -> bool:
+ if preset:
+ preset_val = Menu.yes()
+ else:
+ preset_val = Menu.no()
+
+ prompt = _('Would you like to use unified kernel images?')
+ choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), preset_values=preset_val).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True
+
+ return preset
+
+
+def select_driver(options: List[GfxDriver] = [], current_value: Optional[GfxDriver] = None) -> Optional[GfxDriver]:
+ """
+ Some what convoluted function, whose job is simple.
+ Select a graphics driver from a pre-defined set of popular options.
+
+ (The template xorg is for beginner users, not advanced, and should
+ there for appeal to the general public first and edge cases later)
+ """
+ if not options:
+ options = [driver for driver in GfxDriver]
+
+ drivers = sorted([o.value for o in options])
+
+ if drivers:
+ title = ''
+ if SysInfo.has_amd_graphics():
+ title += str(_('For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.')) + '\n'
+ if SysInfo.has_intel_graphics():
+ title += str(_('For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n'))
+ if SysInfo.has_nvidia_graphics():
+ title += str(_('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n'))
+
+ preset = current_value.value if current_value else None
+
+ choice = Menu(
+ title,
+ drivers,
+ preset_values=preset,
+ default_option=GfxDriver.AllOpenSource.value,
+ preview_command=lambda x: GfxDriver(x).packages_text(),
+ preview_size=0.3
+ ).run()
+
+ if choice.type_ != MenuSelectionType.Selection:
+ return current_value
+
+ return GfxDriver(choice.single_value)
+
+ return current_value
+
+
+def ask_for_swap(preset: bool = True) -> bool:
+ if preset:
+ preset_val = Menu.yes()
+ else:
+ preset_val = Menu.no()
+
+ prompt = _('Would you like to use swap on zram?')
+ choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), preset_values=preset_val).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True
+
+ return preset
diff --git a/archinstall/lib/interactions/utils.py b/archinstall/lib/interactions/utils.py
new file mode 100644
index 00000000..fdbb4625
--- /dev/null
+++ b/archinstall/lib/interactions/utils.py
@@ -0,0 +1,39 @@
+from __future__ import annotations
+
+import getpass
+from typing import Any, Optional, TYPE_CHECKING
+
+from ..models import PasswordStrength
+from ..output import log, error
+
+if TYPE_CHECKING:
+ _: Any
+
+# used for signal handler
+SIG_TRIGGER = None
+
+
+def get_password(prompt: str = '') -> Optional[str]:
+ if not prompt:
+ prompt = _("Enter a password: ")
+
+ while True:
+ try:
+ password = getpass.getpass(prompt)
+ except (KeyboardInterrupt, EOFError):
+ break
+
+ if len(password.strip()) <= 0:
+ break
+
+ strength = PasswordStrength.strength(password)
+ log(f'Password strength: {strength.value}', fg=strength.color())
+
+ passwd_verification = getpass.getpass(prompt=_('And one more time for verification: '))
+ if password != passwd_verification:
+ error(' * Passwords did not match * ')
+ continue
+
+ return password
+
+ return None
diff --git a/archinstall/lib/locale/__init__.py b/archinstall/lib/locale/__init__.py
new file mode 100644
index 00000000..90f1aecc
--- /dev/null
+++ b/archinstall/lib/locale/__init__.py
@@ -0,0 +1,10 @@
+from .locale_menu import LocaleConfiguration
+from .utils import (
+ list_keyboard_languages,
+ list_locales,
+ list_x11_keyboard_languages,
+ verify_keyboard_layout,
+ verify_x11_keyboard_layout,
+ list_timezones,
+ set_kb_layout
+)
diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py
new file mode 100644
index 00000000..db119f20
--- /dev/null
+++ b/archinstall/lib/locale/locale_menu.py
@@ -0,0 +1,158 @@
+from dataclasses import dataclass
+from typing import Dict, Any, TYPE_CHECKING, Optional
+
+from .utils import list_keyboard_languages, list_locales, set_kb_layout
+from ..menu import Selector, AbstractSubMenu, MenuSelectionType, Menu
+
+if TYPE_CHECKING:
+ _: Any
+
+
+@dataclass
+class LocaleConfiguration:
+ kb_layout: str
+ sys_lang: str
+ sys_enc: str
+
+ @staticmethod
+ def default() -> 'LocaleConfiguration':
+ return LocaleConfiguration('us', 'en_US', 'UTF-8')
+
+ def json(self) -> Dict[str, str]:
+ return {
+ 'kb_layout': self.kb_layout,
+ 'sys_lang': self.sys_lang,
+ 'sys_enc': self.sys_enc
+ }
+
+ @classmethod
+ def _load_config(cls, config: 'LocaleConfiguration', args: Dict[str, Any]) -> 'LocaleConfiguration':
+ if 'sys_lang' in args:
+ config.sys_lang = args['sys_lang']
+ if 'sys_enc' in args:
+ config.sys_enc = args['sys_enc']
+ if 'kb_layout' in args:
+ config.kb_layout = args['kb_layout']
+
+ return config
+
+ @classmethod
+ def parse_arg(cls, args: Dict[str, Any]) -> 'LocaleConfiguration':
+ default = cls.default()
+
+ if 'locale_config' in args:
+ default = cls._load_config(default, args['locale_config'])
+ else:
+ default = cls._load_config(default, args)
+
+ return default
+
+
+class LocaleMenu(AbstractSubMenu):
+ def __init__(
+ self,
+ data_store: Dict[str, Any],
+ locale_conf: LocaleConfiguration
+ ):
+ self._preset = locale_conf
+ super().__init__(data_store=data_store)
+
+ def setup_selection_menu_options(self):
+ self._menu_options['keyboard-layout'] = \
+ Selector(
+ _('Keyboard layout'),
+ lambda preset: self._select_kb_layout(preset),
+ default=self._preset.kb_layout,
+ enabled=True)
+ self._menu_options['sys-language'] = \
+ Selector(
+ _('Locale language'),
+ lambda preset: select_locale_lang(preset),
+ default=self._preset.sys_lang,
+ enabled=True)
+ self._menu_options['sys-encoding'] = \
+ Selector(
+ _('Locale encoding'),
+ lambda preset: select_locale_enc(preset),
+ default=self._preset.sys_enc,
+ enabled=True)
+
+ def run(self, allow_reset: bool = True) -> LocaleConfiguration:
+ super().run(allow_reset=allow_reset)
+
+ if not self._data_store:
+ return LocaleConfiguration.default()
+
+ return LocaleConfiguration(
+ self._data_store['keyboard-layout'],
+ self._data_store['sys-language'],
+ self._data_store['sys-encoding']
+ )
+
+ def _select_kb_layout(self, preset: Optional[str]) -> Optional[str]:
+ kb_lang = select_kb_layout(preset)
+ if kb_lang:
+ set_kb_layout(kb_lang)
+ return kb_lang
+
+
+def select_locale_lang(preset: Optional[str] = None) -> Optional[str]:
+ locales = list_locales()
+ locale_lang = set([locale.split()[0] for locale in locales])
+
+ choice = Menu(
+ _('Choose which locale language to use'),
+ list(locale_lang),
+ sort=True,
+ preset_values=preset
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Selection: return choice.single_value
+ case MenuSelectionType.Skip: return preset
+
+ return None
+
+
+def select_locale_enc(preset: Optional[str] = None) -> Optional[str]:
+ locales = list_locales()
+ locale_enc = set([locale.split()[1] for locale in locales])
+
+ choice = Menu(
+ _('Choose which locale encoding to use'),
+ list(locale_enc),
+ sort=True,
+ preset_values=preset
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Selection: return choice.single_value
+ case MenuSelectionType.Skip: return preset
+
+ return None
+
+
+def select_kb_layout(preset: Optional[str] = None) -> Optional[str]:
+ """
+ Asks the user to select a language
+ Usually this is combined with :ref:`archinstall.list_keyboard_languages`.
+
+ :return: The language/dictionary key of the selected language
+ :rtype: str
+ """
+ kb_lang = list_keyboard_languages()
+ # sort alphabetically and then by length
+ sorted_kb_lang = sorted(kb_lang, key=lambda x: (len(x), x))
+
+ choice = Menu(
+ _('Select keyboard layout'),
+ sorted_kb_lang,
+ preset_values=preset,
+ sort=False
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return choice.single_value
+
+ return None
diff --git a/archinstall/lib/locale/utils.py b/archinstall/lib/locale/utils.py
new file mode 100644
index 00000000..d7641d50
--- /dev/null
+++ b/archinstall/lib/locale/utils.py
@@ -0,0 +1,67 @@
+from typing import List
+
+from ..exceptions import ServiceException, SysCallError
+from ..general import SysCommand
+from ..output import error
+
+
+def list_keyboard_languages() -> List[str]:
+ return SysCommand(
+ "localectl --no-pager list-keymaps",
+ environment_vars={'SYSTEMD_COLORS': '0'}
+ ).decode().splitlines()
+
+
+def list_locales() -> List[str]:
+ locales = []
+
+ with open('/usr/share/i18n/SUPPORTED') as file:
+ for line in file:
+ if line != 'C.UTF-8 UTF-8\n':
+ locales.append(line.rstrip())
+
+ return locales
+
+
+def list_x11_keyboard_languages() -> List[str]:
+ return SysCommand(
+ "localectl --no-pager list-x11-keymap-layouts",
+ environment_vars={'SYSTEMD_COLORS': '0'}
+ ).decode().splitlines()
+
+
+def verify_keyboard_layout(layout :str) -> bool:
+ for language in list_keyboard_languages():
+ if layout.lower() == language.lower():
+ return True
+ return False
+
+
+def verify_x11_keyboard_layout(layout :str) -> bool:
+ for language in list_x11_keyboard_languages():
+ if layout.lower() == language.lower():
+ return True
+ return False
+
+
+def set_kb_layout(locale :str) -> bool:
+ if len(locale.strip()):
+ if not verify_keyboard_layout(locale):
+ error(f"Invalid keyboard locale specified: {locale}")
+ return False
+
+ try:
+ SysCommand(f'localectl set-keymap {locale}')
+ except SysCallError as err:
+ raise ServiceException(f"Unable to set locale '{locale}' for console: {err}")
+
+ return True
+
+ return False
+
+
+def list_timezones() -> List[str]:
+ return SysCommand(
+ "timedatectl --no-pager list-timezones",
+ environment_vars={'SYSTEMD_COLORS': '0'}
+ ).decode().splitlines()
diff --git a/archinstall/lib/locale_helpers.py b/archinstall/lib/locale_helpers.py
deleted file mode 100644
index 5580fa91..00000000
--- a/archinstall/lib/locale_helpers.py
+++ /dev/null
@@ -1,168 +0,0 @@
-import logging
-from typing import Iterator, List, Callable
-
-from .exceptions import ServiceException
-from .general import SysCommand
-from .output import log
-from .storage import storage
-
-def list_keyboard_languages() -> Iterator[str]:
- for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}):
- yield line.decode('UTF-8').strip()
-
-
-def list_locales() -> List[str]:
- with open('/etc/locale.gen', 'r') as fp:
- locales = []
- # before the list of locales begins there's an empty line with a '#' in front
- # so we'll collect the localels from bottom up and halt when we're donw
- entries = fp.readlines()
- entries.reverse()
-
- for entry in entries:
- text = entry.replace('#', '').strip()
- if text == '':
- break
- locales.append(text)
-
- locales.reverse()
- return locales
-
-def get_locale_mode_text(mode):
- if mode == 'LC_ALL':
- mode_text = "general (LC_ALL)"
- elif mode == "LC_CTYPE":
- mode_text = "Character set"
- elif mode == "LC_NUMERIC":
- mode_text = "Numeric values"
- elif mode == "LC_TIME":
- mode_text = "Time Values"
- elif mode == "LC_COLLATE":
- mode_text = "sort order"
- elif mode == "LC_MESSAGES":
- mode_text = "text messages"
- else:
- mode_text = "Unassigned"
- return mode_text
-
-def reset_cmd_locale():
- """ sets the cmd_locale to its saved default """
- storage['CMD_LOCALE'] = storage.get('CMD_LOCALE_DEFAULT',{})
-
-def unset_cmd_locale():
- """ archinstall will use the execution environment default """
- storage['CMD_LOCALE'] = {}
-
-def set_cmd_locale(general :str = None,
- charset :str = 'C',
- numbers :str = 'C',
- time :str = 'C',
- collate :str = 'C',
- messages :str = 'C'):
- """
- Set the cmd locale.
- If the parameter general is specified, it takes precedence over the rest (might as well not exist)
- The rest define some specific settings above the installed default language. If anyone of this parameters is none means the installation default
- """
- installed_locales = list_installed_locales()
- result = {}
- if general:
- if general in installed_locales:
- storage['CMD_LOCALE'] = {'LC_ALL':general}
- else:
- log(f"{get_locale_mode_text('LC_ALL')} {general} is not installed. Defaulting to C",fg="yellow",level=logging.WARNING)
- return
-
- if numbers:
- if numbers in installed_locales:
- result["LC_NUMERIC"] = numbers
- else:
- log(f"{get_locale_mode_text('LC_NUMERIC')} {numbers} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if charset:
- if charset in installed_locales:
- result["LC_CTYPE"] = charset
- else:
- log(f"{get_locale_mode_text('LC_CTYPE')} {charset} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if time:
- if time in installed_locales:
- result["LC_TIME"] = time
- else:
- log(f"{get_locale_mode_text('LC_TIME')} {time} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if collate:
- if collate in installed_locales:
- result["LC_COLLATE"] = collate
- else:
- log(f"{get_locale_mode_text('LC_COLLATE')} {collate} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if messages:
- if messages in installed_locales:
- result["LC_MESSAGES"] = messages
- else:
- log(f"{get_locale_mode_text('LC_MESSAGES')} {messages} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- storage['CMD_LOCALE'] = result
-
-def host_locale_environ(func :Callable):
- """ decorator when we want a function executing in the host's locale environment """
- def wrapper(*args, **kwargs):
- unset_cmd_locale()
- result = func(*args,**kwargs)
- reset_cmd_locale()
- return result
- return wrapper
-
-def c_locale_environ(func :Callable):
- """ decorator when we want a function executing in the C locale environment """
- def wrapper(*args, **kwargs):
- set_cmd_locale(general='C')
- result = func(*args,**kwargs)
- reset_cmd_locale()
- return result
- return wrapper
-
-def list_installed_locales() -> List[str]:
- lista = []
- for line in SysCommand('locale -a'):
- lista.append(line.decode('UTF-8').strip())
- return lista
-
-def list_x11_keyboard_languages() -> Iterator[str]:
- for line in SysCommand("localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'}):
- yield line.decode('UTF-8').strip()
-
-
-def verify_keyboard_layout(layout :str) -> bool:
- for language in list_keyboard_languages():
- if layout.lower() == language.lower():
- return True
- return False
-
-
-def verify_x11_keyboard_layout(layout :str) -> bool:
- for language in list_x11_keyboard_languages():
- if layout.lower() == language.lower():
- return True
- return False
-
-
-def search_keyboard_layout(layout :str) -> Iterator[str]:
- for language in list_keyboard_languages():
- if layout.lower() in language.lower():
- yield language
-
-
-def set_keyboard_language(locale :str) -> bool:
- if len(locale.strip()):
- if not verify_keyboard_layout(locale):
- log(f"Invalid keyboard locale specified: {locale}", fg="red", level=logging.ERROR)
- return False
-
- if (output := SysCommand(f'localectl set-keymap {locale}')).exit_code != 0:
- raise ServiceException(f"Unable to set locale '{locale}' for console: {output}")
-
- return True
-
- return False
-
-
-def list_timezones() -> Iterator[str]:
- for line in SysCommand("timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'}):
- yield line.decode('UTF-8').strip()
diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py
index ad6bf093..50e15cee 100644
--- a/archinstall/lib/luks.py
+++ b/archinstall/lib/luks.py
@@ -1,92 +1,77 @@
from __future__ import annotations
-import json
-import logging
-import os
-import pathlib
+
import shlex
import time
-from typing import Optional, List,TYPE_CHECKING
-# https://stackoverflow.com/a/39757388/929999
-if TYPE_CHECKING:
- from .installer import Installer
-
-from .disk import Partition, convert_device_to_uuid
-from .general import SysCommand, SysCommandWorker
-from .output import log
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Optional, List
+
+from . import disk
+from .general import SysCommand, generate_password, SysCommandWorker
+from .output import info, debug
from .exceptions import SysCallError, DiskError
from .storage import storage
-from .disk.helpers import get_filesystem_type
-from .disk.mapperdev import MapperDev
-from .disk.btrfs import BTRFSPartition
-
-
-class luks2:
- def __init__(self,
- partition: Partition,
- mountpoint: Optional[str],
- password: Optional[str],
- key_file :Optional[str] = None,
- auto_unmount :bool = False,
- *args :str,
- **kwargs :str):
-
- self.password = password
- self.partition = partition
- self.mountpoint = mountpoint
- self.args = args
- self.kwargs = kwargs
- self.key_file = key_file
- self.auto_unmount = auto_unmount
- self.filesystem = 'crypto_LUKS'
- self.mapdev = None
- def __enter__(self) -> Partition:
- if not self.key_file:
- self.key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique?
- if type(self.password) != bytes:
- self.password = bytes(self.password, 'UTF-8')
+@dataclass
+class Luks2:
+ luks_dev_path: Path
+ mapper_name: Optional[str] = None
+ password: Optional[str] = None
+ key_file: Optional[Path] = None
+ auto_unmount: bool = False
- with open(self.key_file, 'wb') as fh:
- fh.write(self.password)
+ # will be set internally after unlocking the device
+ _mapper_dev: Optional[Path] = None
- return self.unlock(self.partition, self.mountpoint, self.key_file)
+ @property
+ def mapper_dev(self) -> Optional[Path]:
+ if self.mapper_name:
+ return Path(f'/dev/mapper/{self.mapper_name}')
+ return None
- def __exit__(self, *args :str, **kwargs :str) -> bool:
- # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
+ def __post_init__(self):
+ if self.luks_dev_path is None:
+ raise ValueError('Partition must have a path set')
+
+ def __enter__(self):
+ self.unlock(self.key_file)
+
+ def __exit__(self, *args: str, **kwargs: str):
if self.auto_unmount:
- self.close()
+ self.lock()
+
+ def _default_key_file(self) -> Path:
+ return Path(f'/tmp/{self.luks_dev_path.name}.disk_pw')
- if len(args) >= 2 and args[1]:
- raise args[1]
+ def _password_bytes(self) -> bytes:
+ if not self.password:
+ raise ValueError('Password for luks2 device was not specified')
- return True
+ if isinstance(self.password, bytes):
+ return self.password
+ else:
+ return bytes(self.password, 'UTF-8')
- def encrypt(self, partition :Partition,
- password :Optional[str] = None,
- key_size :int = 512,
- hash_type :str = 'sha512',
- iter_time :int = 10000,
- key_file :Optional[str] = None) -> str:
+ def encrypt(
+ self,
+ key_size: int = 512,
+ hash_type: str = 'sha512',
+ iter_time: int = 10000,
+ key_file: Optional[Path] = None
+ ) -> Path:
+ debug(f'Luks2 encrypting: {self.luks_dev_path}')
- log(f'Encrypting {partition} (This might take a while)', level=logging.INFO)
+ byte_password = self._password_bytes()
if not key_file:
if self.key_file:
key_file = self.key_file
else:
- key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique?
+ key_file = self._default_key_file()
- if not password:
- password = self.password
-
- if type(password) != bytes:
- password = bytes(password, 'UTF-8')
-
- with open(key_file, 'wb') as fh:
- fh.write(password)
-
- partition.partprobe()
+ with open(key_file, 'wb') as fh:
+ fh.write(byte_password)
cryptsetup_args = shlex.join([
'/usr/bin/cryptsetup',
@@ -97,120 +82,170 @@ class luks2:
'--hash', hash_type,
'--key-size', str(key_size),
'--iter-time', str(iter_time),
- '--key-file', os.path.abspath(key_file),
+ '--key-file', str(key_file),
'--use-urandom',
- 'luksFormat', partition.path,
+ 'luksFormat', str(self.luks_dev_path),
])
- try:
- # Retry formatting the volume because archinstall can some times be too quick
- # which generates a "Device /dev/sdX does not exist or access denied." between
- # setting up partitions and us trying to encrypt it.
- for i in range(storage['DISK_RETRY_ATTEMPTS']):
- if (cmd_handle := SysCommand(cryptsetup_args)).exit_code != 0:
- time.sleep(storage['DISK_TIMEOUTS'])
+ debug(f'cryptsetup format: {cryptsetup_args}')
+
+ # Retry formatting the volume because archinstall can some times be too quick
+ # which generates a "Device /dev/sdX does not exist or access denied." between
+ # setting up partitions and us trying to encrypt it.
+ for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS'] + 1):
+ try:
+ result = SysCommand(cryptsetup_args).decode()
+ debug(f'cryptsetup luksFormat output: {result}')
+ break
+ except SysCallError as err:
+ time.sleep(storage['DISK_TIMEOUTS'])
+
+ if retry_attempt != storage['DISK_RETRY_ATTEMPTS']:
+ continue
+
+ if err.exit_code == 1:
+ info(f'luks2 partition currently in use: {self.luks_dev_path}')
+ info('Attempting to unmount, crypt-close and trying encryption again')
+
+ self.lock()
+ # Then try again to set up the crypt-device
+ result = SysCommand(cryptsetup_args).decode()
+ debug(f'cryptsetup luksFormat output: {result}')
else:
- break
+ raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {err}')
- if cmd_handle.exit_code != 0:
- raise DiskError(f'Could not encrypt volume "{partition.path}": {b"".join(cmd_handle)}')
- except SysCallError as err:
- if err.exit_code == 1:
- log(f'{partition} is being used, trying to unmount and crypt-close the device and running one more attempt at encrypting the device.', level=logging.DEBUG)
- # Partition was in use, unmount it and try again
- partition.unmount()
-
- # Get crypt-information about the device by doing a reverse lookup starting with the partition path
- # For instance: /dev/sda
- SysCommand(f'bash -c "partprobe"')
- devinfo = json.loads(b''.join(SysCommand(f"lsblk --fs -J {partition.path}")).decode('UTF-8'))['blockdevices'][0]
-
- # For each child (sub-partition/sub-device)
- if len(children := devinfo.get('children', [])):
- for child in children:
- # Unmount the child location
- if child_mountpoint := child.get('mountpoint', None):
- log(f'Unmounting {child_mountpoint}', level=logging.DEBUG)
- SysCommand(f"umount -R {child_mountpoint}")
-
- # And close it if possible.
- log(f"Closing crypt device {child['name']}", level=logging.DEBUG)
- SysCommand(f"cryptsetup close {child['name']}")
-
- # Then try again to set up the crypt-device
- cmd_handle = SysCommand(cryptsetup_args)
- else:
- raise err
+ self.key_file = key_file
return key_file
- def unlock(self, partition :Partition, mountpoint :str, key_file :str) -> Partition:
+ def _get_luks_uuid(self) -> str:
+ command = f'/usr/bin/cryptsetup luksUUID {self.luks_dev_path}'
+
+ try:
+ return SysCommand(command).decode()
+ except SysCallError as err:
+ info(f'Unable to get UUID for Luks device: {self.luks_dev_path}')
+ raise err
+
+ def is_unlocked(self) -> bool:
+ return self.mapper_name is not None and Path(f'/dev/mapper/{self.mapper_name}').exists()
+
+ def unlock(self, key_file: Optional[Path] = None):
"""
- Mounts a luks2 compatible partition to a certain mountpoint.
- Keyfile must be specified as there's no way to interact with the pw-prompt atm.
+ Unlocks the luks device, an optional key file location for unlocking can be specified,
+ otherwise a default location for the key file will be used.
- :param mountpoint: The name without absolute path, for instance "luksdev" will point to /dev/mapper/luksdev
- :type mountpoint: str
+ :param key_file: An alternative key file
+ :type key_file: Path
"""
+ debug(f'Unlocking luks2 device: {self.luks_dev_path}')
- if '/' in mountpoint:
- os.path.basename(mountpoint) # TODO: Raise exception instead?
+ if not self.mapper_name:
+ raise ValueError('mapper name missing')
+
+ byte_password = self._password_bytes()
+
+ if not key_file:
+ if self.key_file:
+ key_file = self.key_file
+ else:
+ key_file = self._default_key_file()
+
+ with open(key_file, 'wb') as fh:
+ fh.write(byte_password)
wait_timer = time.time()
- while pathlib.Path(partition.path).exists() is False and time.time() - wait_timer < 10:
+ while Path(self.luks_dev_path).exists() is False and time.time() - wait_timer < 10:
time.sleep(0.025)
- SysCommand(f'/usr/bin/cryptsetup open {partition.path} {mountpoint} --key-file {os.path.abspath(key_file)} --type luks2')
- if os.path.islink(f'/dev/mapper/{mountpoint}'):
- self.mapdev = f'/dev/mapper/{mountpoint}'
-
- if (filesystem_type := get_filesystem_type(pathlib.Path(self.mapdev))) == 'btrfs':
- return BTRFSPartition(
- self.mapdev,
- block_device=MapperDev(mountpoint).partition.block_device,
- encrypted=True,
- filesystem=filesystem_type,
- autodetect_filesystem=False
- )
-
- return Partition(
- self.mapdev,
- block_device=MapperDev(mountpoint).partition.block_device,
- encrypted=True,
- filesystem=get_filesystem_type(self.mapdev),
- autodetect_filesystem=False
- )
-
- def close(self, mountpoint :Optional[str] = None) -> bool:
- if not mountpoint:
- mountpoint = self.mapdev
-
- SysCommand(f'/usr/bin/cryptsetup close {self.mapdev}')
- return os.path.islink(self.mapdev) is False
-
- def format(self, path :str) -> None:
- if (handle := SysCommand(f"/usr/bin/cryptsetup -q -v luksErase {path}")).exit_code != 0:
- raise DiskError(f'Could not format {path} with {self.filesystem} because: {b"".join(handle)}')
-
- def add_key(self, path :pathlib.Path, password :str) -> bool:
- if not path.exists():
- raise OSError(2, f"Could not import {path} as a disk encryption key, file is missing.", str(path))
-
- log(f'Adding additional key-file {path} for {self.partition}', level=logging.INFO)
- worker = SysCommandWorker(f"/usr/bin/cryptsetup -q -v luksAddKey {self.partition.path} {path}",
- environment_vars={'LC_ALL':'C'})
+ result = SysCommand(
+ '/usr/bin/cryptsetup open '
+ f'{self.luks_dev_path} '
+ f'{self.mapper_name} '
+ f'--key-file {key_file} '
+ f'--type luks2'
+ ).decode()
+
+ debug(f'cryptsetup open output: {result}')
+
+ if not self.mapper_dev or not self.mapper_dev.is_symlink():
+ raise DiskError(f'Failed to open luks2 device: {self.luks_dev_path}')
+
+ def lock(self):
+ disk.device_handler.umount(self.luks_dev_path)
+
+ # Get crypt-information about the device by doing a reverse lookup starting with the partition path
+ # For instance: /dev/sda
+ lsblk_info = disk.get_lsblk_info(self.luks_dev_path)
+
+ # For each child (sub-partition/sub-device)
+ for child in lsblk_info.children:
+ # Unmount the child location
+ for mountpoint in child.mountpoints:
+ debug(f'Unmounting {mountpoint}')
+ disk.device_handler.umount(mountpoint, recursive=True)
+
+ # And close it if possible.
+ debug(f"Closing crypt device {child.name}")
+ SysCommand(f"cryptsetup close {child.name}")
+
+ self._mapper_dev = None
+
+ def create_keyfile(self, target_path: Path, override: bool = False):
+ """
+ Routine to create keyfiles, so it can be moved elsewhere
+ """
+ if self.mapper_name is None:
+ raise ValueError('Mapper name must be provided')
+
+ # Once we store the key as ../xyzloop.key systemd-cryptsetup can
+ # automatically load this key if we name the device to "xyzloop"
+ kf_path = Path(f'/etc/cryptsetup-keys.d/{self.mapper_name}.key')
+ key_file = target_path / kf_path.relative_to(kf_path.root)
+ crypttab_path = target_path / 'etc/crypttab'
+
+ if key_file.exists():
+ if not override:
+ info(f'Key file {key_file} already exists, keeping existing')
+ return
+ else:
+ info(f'Key file {key_file} already exists, overriding')
+
+ key_file.parent.mkdir(parents=True, exist_ok=True)
+
+ pwd = generate_password(length=512)
+ key_file.write_text(pwd)
+
+ key_file.chmod(0o400)
+
+ self._add_key(key_file)
+ self._crypttab(crypttab_path, kf_path, options=["luks", "key-slot=1"])
+
+ def _add_key(self, key_file: Path):
+ debug(f'Adding additional key-file {key_file}')
+
+ command = f'/usr/bin/cryptsetup -q -v luksAddKey {self.luks_dev_path} {key_file}'
+ worker = SysCommandWorker(command, environment_vars={'LC_ALL': 'C'})
pw_injected = False
+
while worker.is_alive():
if b'Enter any existing passphrase' in worker and pw_injected is False:
- worker.write(bytes(password, 'UTF-8'))
+ worker.write(self._password_bytes())
pw_injected = True
if worker.exit_code != 0:
- raise DiskError(f'Could not add encryption key {path} to {self.partition} because: {worker}')
-
- return True
-
- def crypttab(self, installation :Installer, key_path :str, options :List[str] = ["luks", "key-slot=1"]) -> None:
- log(f'Adding a crypttab entry for key {key_path} in {installation}', level=logging.INFO)
- with open(f"{installation.target}/etc/crypttab", "a") as crypttab:
- crypttab.write(f"{self.mountpoint} UUID={convert_device_to_uuid(self.partition.path)} {key_path} {','.join(options)}\n")
+ raise DiskError(f'Could not add encryption key {key_file} to {self.luks_dev_path}: {worker.decode()}')
+
+ def _crypttab(
+ self,
+ crypttab_path: Path,
+ key_file: Path,
+ options: List[str]
+ ) -> None:
+ debug(f'Adding crypttab entry for key {key_file}')
+
+ with open(crypttab_path, 'a') as crypttab:
+ opt = ','.join(options)
+ uuid = self._get_luks_uuid()
+ row = f"{self.mapper_name} UUID={uuid} {key_file} {opt}\n"
+ crypttab.write(row)
diff --git a/archinstall/lib/menu/__init__.py b/archinstall/lib/menu/__init__.py
index 9b0adb8b..9c86faf5 100644
--- a/archinstall/lib/menu/__init__.py
+++ b/archinstall/lib/menu/__init__.py
@@ -1,2 +1,9 @@
-from .menu import Menu as Menu
-from .global_menu import GlobalMenu as GlobalMenu \ No newline at end of file
+from .abstract_menu import Selector, AbstractMenu, AbstractSubMenu
+from .list_manager import ListManager
+from .menu import (
+ MenuSelectionType,
+ MenuSelection,
+ Menu,
+)
+from .table_selection_menu import TableMenu
+from .text_input import TextInput
diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py
index d659d709..ee55f5c9 100644
--- a/archinstall/lib/menu/abstract_menu.py
+++ b/archinstall/lib/menu/abstract_menu.py
@@ -1,13 +1,11 @@
from __future__ import annotations
-import logging
from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING
from .menu import Menu, MenuSelectionType
-from ..locale_helpers import set_keyboard_language
-from ..output import log
+from ..output import error
+from ..output import unicode_ljust
from ..translationhandler import TranslationHandler, Language
-from ..user_interaction.general_conf import select_archinstall_language
if TYPE_CHECKING:
_: Any
@@ -16,17 +14,17 @@ if TYPE_CHECKING:
class Selector:
def __init__(
self,
- description :str,
- func :Optional[Callable] = None,
- display_func :Optional[Callable] = None,
- default :Any = None,
- enabled :bool = False,
- dependencies :List = [],
- dependencies_not :List = [],
- exec_func :Optional[Callable] = None,
- preview_func :Optional[Callable] = None,
- mandatory :bool = False,
- no_store :bool = False
+ description: str,
+ func: Optional[Callable[[Any], Any]] = None,
+ display_func: Optional[Callable] = None,
+ default: Optional[Any] = None,
+ enabled: bool = False,
+ dependencies: List = [],
+ dependencies_not: List = [],
+ exec_func: Optional[Callable] = None,
+ preview_func: Optional[Callable] = None,
+ mandatory: bool = False,
+ no_store: bool = False
):
"""
Create a new menu selection entry
@@ -71,84 +69,66 @@ class Selector:
:param no_store: A boolean which determines that the field should or shouldn't be stored in the data storage
:type no_store: bool
"""
- self._description = description
- self.func = func
self._display_func = display_func
- self._current_selection = default
+ self._no_store = no_store
+
+ self.description = description
+ self.func = func
+ self.current_selection = default
self.enabled = enabled
- self._dependencies = dependencies
- self._dependencies_not = dependencies_not
+ self.dependencies = dependencies
+ self.dependencies_not = dependencies_not
self.exec_func = exec_func
- self._preview_func = preview_func
+ self.preview_func = preview_func
self.mandatory = mandatory
- self._no_store = no_store
-
- @property
- def description(self) -> str:
- return self._description
-
- @property
- def dependencies(self) -> List:
- return self._dependencies
-
- @property
- def dependencies_not(self) -> List:
- return self._dependencies_not
-
- @property
- def current_selection(self):
- return self._current_selection
-
- @property
- def preview_func(self):
- return self._preview_func
+ self.default = default
def do_store(self) -> bool:
return self._no_store is False
- def set_enabled(self, status :bool = True):
+ def set_enabled(self, status: bool = True):
self.enabled = status
- def update_description(self, description :str):
- self._description = description
+ def update_description(self, description: str):
+ self.description = description
def menu_text(self, padding: int = 0) -> str:
- if self._description == '': # special menu option for __separator__
+ if self.description == '': # special menu option for __separator__
return ''
current = ''
if self._display_func:
- current = self._display_func(self._current_selection)
+ current = self._display_func(self.current_selection)
else:
- if self._current_selection is not None:
- current = str(self._current_selection)
+ if self.current_selection is not None:
+ current = str(self.current_selection)
if current:
padding += 5
- description = str(self._description).ljust(padding, ' ')
- current = str(_('set: {}').format(current))
+ description = unicode_ljust(str(self.description), padding, ' ')
+ current = current
else:
- description = self._description
+ description = self.description
current = ''
return f'{description} {current}'
- def set_current_selection(self, current :Optional[str]):
- self._current_selection = current
+ def set_current_selection(self, current: Optional[Any]):
+ self.current_selection = current
def has_selection(self) -> bool:
- if not self._current_selection:
+ if not self.current_selection:
return False
return True
def get_selection(self) -> Any:
- return self._current_selection
+ return self.current_selection
def is_empty(self) -> bool:
- if self._current_selection is None:
+ if self.current_selection is None:
return True
- elif isinstance(self._current_selection, (str, list, dict)) and len(self._current_selection) == 0:
+ elif isinstance(self.current_selection, (str, list, dict)) and len(self.current_selection) == 0:
return True
return False
@@ -158,14 +138,17 @@ class Selector:
def is_mandatory(self) -> bool:
return self.mandatory
- def set_mandatory(self, status :bool = True):
- self.mandatory = status
- if status and not self.is_enabled():
- self.set_enabled(True)
+ def set_mandatory(self, value: bool):
+ self.mandatory = value
class AbstractMenu:
- def __init__(self, data_store: Optional[Dict[str, Any]] = None, auto_cursor=False, preview_size :float = 0.2):
+ def __init__(
+ self,
+ data_store: Dict[str, Any] = {},
+ auto_cursor: bool = False,
+ preview_size: float = 0.2
+ ):
"""
Create a new selection menu.
@@ -179,29 +162,34 @@ class AbstractMenu:
;type preview_size: float (range 0..1)
"""
- self._enabled_order :List[str] = []
+ self._enabled_order: List[str] = []
self._translation_handler = TranslationHandler()
self.is_context_mgr = False
- self._data_store = data_store if data_store is not None else {}
+ self._data_store = data_store
self.auto_cursor = auto_cursor
self._menu_options: Dict[str, Selector] = {}
- self._setup_selection_menu_options()
self.preview_size = preview_size
self._last_choice = None
+ self.setup_selection_menu_options()
+ self._sync_all()
+ self._populate_default_values()
+
+ self.defined_text = str(_('Defined'))
+
@property
def last_choice(self):
return self._last_choice
- def __enter__(self, *args :Any, **kwargs :Any) -> AbstractMenu:
+ def __enter__(self, *args: Any, **kwargs: Any) -> AbstractMenu:
self.is_context_mgr = True
return self
- def __exit__(self, *args :Any, **kwargs :Any) -> None:
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
# TODO: skip processing when it comes from a planified exit
if len(args) >= 2 and args[1]:
- log(args[1], level=logging.ERROR, fg='red')
+ error(args[1])
print(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues")
raise args[1]
@@ -216,7 +204,25 @@ class AbstractMenu:
def translation_handler(self) -> TranslationHandler:
return self._translation_handler
- def _setup_selection_menu_options(self):
+ def _populate_default_values(self):
+ for config_key, selector in self._menu_options.items():
+ if selector.default is not None and config_key not in self._data_store:
+ self._data_store[config_key] = selector.default
+
+ def _sync_all(self):
+ for key in self._menu_options.keys():
+ self._sync(key)
+
+ def _sync(self, selector_name: str):
+ value = self._data_store.get(selector_name, None)
+ selector = self._menu_options.get(selector_name, None)
+
+ if value is not None:
+ self._menu_options[selector_name].set_current_selection(value)
+ elif selector is not None and selector.has_selection():
+ self._data_store[selector_name] = selector.current_selection
+
+ def setup_selection_menu_options(self):
""" Define the menu options.
Menu options can be defined here in a subclass or done per program calling self.set_option()
"""
@@ -234,31 +240,16 @@ class AbstractMenu:
""" will be called at the end of the processing of the menu """
return
- def synch(self, selector_name :str, omit_if_set :bool = False,omit_if_disabled :bool = False):
- """ loads menu options with data_store value """
- arg = self._data_store.get(selector_name, None)
- # don't display the menu option if it was defined already
- if arg is not None and omit_if_set:
- return
-
- if not self.option(selector_name).is_enabled() and omit_if_disabled:
- return
-
- if arg is not None:
- self._menu_options[selector_name].set_current_selection(arg)
-
def _update_enabled_order(self, selector_name: str):
self._enabled_order.append(selector_name)
- def enable(self, selector_name :str, omit_if_set :bool = False , mandatory :bool = False):
+ def enable(self, selector_name: str, mandatory: bool = False):
""" activates menu options """
if self._menu_options.get(selector_name, None):
self._menu_options[selector_name].set_enabled(True)
self._update_enabled_order(selector_name)
-
- if mandatory:
- self._menu_options[selector_name].set_mandatory(True)
- self.synch(selector_name,omit_if_set)
+ self._menu_options[selector_name].set_mandatory(mandatory)
+ self._sync(selector_name)
else:
raise ValueError(f'No selector found: {selector_name}')
@@ -274,7 +265,11 @@ class AbstractMenu:
def _find_selection(self, selection_name: str) -> Tuple[str, Selector]:
enabled_menus = self._menus_to_enable()
padding = self._get_menu_text_padding(list(enabled_menus.values()))
- option = [(k, v) for k, v in self._menu_options.items() if v.menu_text(padding).strip() == selection_name.strip()]
+
+ option = []
+ for k, v in self._menu_options.items():
+ if v.menu_text(padding).strip() == selection_name.strip():
+ option.append((k, v))
if len(option) != 1:
raise ValueError(f'Selection not found: {selection_name}')
@@ -283,18 +278,11 @@ class AbstractMenu:
return config_name, selector
def run(self, allow_reset: bool = False):
- """ Calls the Menu framework"""
- # we synch all the options just in case
- for item in self.list_options():
- self.synch(item)
-
- self.post_callback() # as all the values can vary i have to exec this callback
+ self._sync_all()
+ self.post_callback()
cursor_pos = None
while True:
- # Before continuing, set the preferred keyboard layout/language in the current terminal.
- # This will just help the user with the next following questions.
- self._set_kb_language()
enabled_menus = self._menus_to_enable()
padding = self._get_menu_text_padding(list(enabled_menus.values()))
@@ -336,18 +324,18 @@ class AbstractMenu:
value = value.strip()
# if this calls returns false, we exit the menu
- # we allow for an callback for special processing on realeasing control
+ # we allow for an callback for special processing on releasing control
if not self._process_selection(value):
break
# we get the last action key
- actions = {str(v.description):k for k,v in self._menu_options.items()}
+ actions = {str(v.description): k for k, v in self._menu_options.items()}
self._last_choice = actions[selection.value.strip()] # type: ignore
if not self.is_context_mgr:
self.__exit__()
- def _process_selection(self, selection_name :str) -> bool:
+ def _process_selection(self, selection_name: str) -> bool:
""" determines and executes the selection y
Can / Should be extended to handle specific selection issues
Returns true if the menu shall continue, False if it has ended
@@ -356,7 +344,7 @@ class AbstractMenu:
config_name, selector = self._find_selection(selection_name)
return self.exec_option(config_name, selector)
- def exec_option(self, config_name :str, p_selector :Optional[Selector] = None) -> bool:
+ def exec_option(self, config_name: str, p_selector: Optional[Selector] = None) -> bool:
""" processes the execution of a given menu entry
- pre process callback
- selection function
@@ -372,40 +360,42 @@ class AbstractMenu:
self.pre_callback(config_name)
result = None
+
if selector.func is not None:
- presel_val = self.option(config_name).get_selection()
- result = selector.func(presel_val)
+ cur_value = self.option(config_name).get_selection()
+ result = selector.func(cur_value)
self._menu_options[config_name].set_current_selection(result)
+
if selector.do_store():
self._data_store[config_name] = result
- exec_ret_val = selector.exec_func(config_name,result) if selector.exec_func is not None else False
- self.post_callback(config_name,result)
- if exec_ret_val and self._check_mandatory_status():
+ exec_ret_val = selector.exec_func(config_name, result) if selector.exec_func else False
+
+ self.post_callback(config_name, result)
+
+ if exec_ret_val:
return False
- return True
- def _set_kb_language(self):
- """ general for ArchInstall"""
- # Before continuing, set the preferred keyboard layout/language in the current terminal.
- # This will just help the user with the next following questions.
- if self._data_store.get('keyboard-layout', None) and len(self._data_store['keyboard-layout']):
- set_keyboard_language(self._data_store['keyboard-layout'])
+ return True
- def _verify_selection_enabled(self, selection_name :str) -> bool:
- """ general """
+ def _verify_selection_enabled(self, selection_name: str) -> bool:
if selection := self._menu_options.get(selection_name, None):
if not selection.enabled:
return False
if len(selection.dependencies) > 0:
- for d in selection.dependencies:
- if not self._verify_selection_enabled(d) or self._menu_options[d].is_empty():
- return False
+ for dep in selection.dependencies:
+ if isinstance(dep, str):
+ if not self._verify_selection_enabled(dep) or self._menu_options[dep].is_empty():
+ return False
+ elif callable(dep): # callable dependency eval
+ return dep()
+ else:
+ raise ValueError(f'Unsupported dependency: {selection_name}')
if len(selection.dependencies_not) > 0:
- for d in selection.dependencies_not:
- if not self._menu_options[d].is_empty():
+ for dep in selection.dependencies_not:
+ if not self._menu_options[dep].is_empty():
return False
return True
@@ -429,16 +419,10 @@ class AbstractMenu:
return ordered_menus
- def option(self,name :str) -> Selector:
+ def option(self, name: str) -> Selector:
# TODO check inexistent name
return self._menu_options[name]
- def list_options(self) -> Iterator:
- """ Iterator to retrieve the enabled menu option names
- """
- for item in self._menu_options:
- yield item
-
def list_enabled_options(self) -> Iterator:
""" Iterator to retrieve the enabled menu options at a given time.
The results are dynamic (if between calls to the iterator some elements -still not retrieved- are (de)activated
@@ -447,44 +431,21 @@ class AbstractMenu:
if item in self._menus_to_enable():
yield item
- def set_option(self, name :str, selector :Selector):
- self._menu_options[name] = selector
- self.synch(name)
-
- def _check_mandatory_status(self) -> bool:
- for field in self._menu_options:
- option = self._menu_options[field]
- if option.is_mandatory() and not option.has_selection():
- return False
- return True
-
- def set_mandatory(self, field :str, status :bool):
- self.option(field).set_mandatory(status)
-
- def mandatory_overview(self) -> Tuple[int, int]:
- mandatory_fields = 0
- mandatory_waiting = 0
- for field, option in self._menu_options.items():
- if option.is_mandatory():
- mandatory_fields += 1
- if not option.has_selection():
- mandatory_waiting += 1
- return mandatory_fields, mandatory_waiting
-
- def _select_archinstall_language(self, preset_value: Language) -> Language:
- language = select_archinstall_language(self.translation_handler.translated_languages, preset_value)
+ def _select_archinstall_language(self, preset: Language) -> Language:
+ from ..interactions.general_conf import select_archinstall_language
+ language = select_archinstall_language(self.translation_handler.translated_languages, preset)
self._translation_handler.activate(language)
return language
class AbstractSubMenu(AbstractMenu):
- def __init__(self, data_store: Optional[Dict[str, Any]] = None):
- super().__init__(data_store=data_store)
+ def __init__(self, data_store: Dict[str, Any] = {}, preview_size: float = 0.2):
+ super().__init__(data_store=data_store, preview_size=preview_size)
self._menu_options['__separator__'] = Selector('')
self._menu_options['back'] = \
Selector(
- _('Back'),
+ Menu.back(),
no_store=True,
enabled=True,
exec_func=lambda n, v: True,
diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py
deleted file mode 100644
index 7c5b153e..00000000
--- a/archinstall/lib/menu/global_menu.py
+++ /dev/null
@@ -1,429 +0,0 @@
-from __future__ import annotations
-
-from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING
-
-import archinstall
-from ..disk.encryption import DiskEncryptionMenu
-from ..general import SysCommand, secret
-from ..hardware import has_uefi
-from ..menu import Menu
-from ..menu.abstract_menu import Selector, AbstractMenu
-from ..models import NetworkConfiguration
-from ..models.disk_encryption import DiskEncryption, EncryptionType
-from ..models.users import User
-from ..output import FormattedOutput
-from ..profiles import is_desktop_profile, Profile
-from ..storage import storage
-from ..user_interaction import add_number_of_parrallel_downloads
-from ..user_interaction import ask_additional_packages_to_install
-from ..user_interaction import ask_for_additional_users
-from ..user_interaction import ask_for_audio_selection
-from ..user_interaction import ask_for_bootloader
-from ..user_interaction import ask_for_swap
-from ..user_interaction import ask_hostname
-from ..user_interaction import ask_ntp
-from ..user_interaction import ask_to_configure_network
-from ..user_interaction import get_password, ask_for_a_timezone, save_config
-from ..user_interaction import select_additional_repositories
-from ..user_interaction import select_disk_layout
-from ..user_interaction import select_harddrives
-from ..user_interaction import select_kernel
-from ..user_interaction import select_language
-from ..user_interaction import select_locale_enc
-from ..user_interaction import select_locale_lang
-from ..user_interaction import select_mirror_regions
-from ..user_interaction import select_profile
-from ..user_interaction.partitioning_conf import current_partition_layout
-
-if TYPE_CHECKING:
- _: Any
-
-
-class GlobalMenu(AbstractMenu):
- def __init__(self,data_store):
- self._disk_check = True
- super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3)
-
- def _setup_selection_menu_options(self):
- # archinstall.Language will not use preset values
- self._menu_options['archinstall-language'] = \
- Selector(
- _('Archinstall language'),
- lambda x: self._select_archinstall_language(x),
- display_func=lambda x: x.display_name,
- default=self.translation_handler.get_language_by_abbr('en'))
- self._menu_options['keyboard-layout'] = \
- Selector(
- _('Keyboard layout'),
- lambda preset: select_language(preset),
- default='us')
- self._menu_options['mirror-region'] = \
- Selector(
- _('Mirror region'),
- lambda preset: select_mirror_regions(preset),
- display_func=lambda x: list(x.keys()) if x else '[]',
- default={})
- self._menu_options['sys-language'] = \
- Selector(
- _('Locale language'),
- lambda preset: select_locale_lang(preset),
- default='en_US')
- self._menu_options['sys-encoding'] = \
- Selector(
- _('Locale encoding'),
- lambda preset: select_locale_enc(preset),
- default='UTF-8')
- self._menu_options['harddrives'] = \
- Selector(
- _('Drive(s)'),
- lambda preset: self._select_harddrives(preset),
- display_func=lambda x: f'{len(x)} ' + str(_('Drive(s)')) if x is not None and len(x) > 0 else '',
- preview_func=self._prev_harddrives,
- )
- self._menu_options['disk_layouts'] = \
- Selector(
- _('Disk layout'),
- lambda preset: select_disk_layout(
- preset,
- storage['arguments'].get('harddrives', []),
- storage['arguments'].get('advanced', False)
- ),
- preview_func=self._prev_disk_layouts,
- display_func=lambda x: self._display_disk_layout(x),
- dependencies=['harddrives'])
- self._menu_options['disk_encryption'] = \
- Selector(
- _('Disk encryption'),
- lambda preset: self._disk_encryption(preset),
- preview_func=self._prev_disk_encryption,
- display_func=lambda x: self._display_disk_encryption(x),
- dependencies=['disk_layouts'])
- self._menu_options['swap'] = \
- Selector(
- _('Swap'),
- lambda preset: ask_for_swap(preset),
- default=True)
- self._menu_options['bootloader'] = \
- Selector(
- _('Bootloader'),
- lambda preset: ask_for_bootloader(storage['arguments'].get('advanced', False),preset),
- default="systemd-bootctl" if has_uefi() else "grub-install")
- self._menu_options['hostname'] = \
- Selector(
- _('Hostname'),
- ask_hostname,
- default='archlinux')
- # root password won't have preset value
- self._menu_options['!root-password'] = \
- Selector(
- _('Root password'),
- lambda preset:self._set_root_password(),
- display_func=lambda x: secret(x) if x else 'None')
- self._menu_options['!users'] = \
- Selector(
- _('User account'),
- lambda x: self._create_user_account(x),
- default={},
- display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else None,
- preview_func=self._prev_users)
- self._menu_options['profile'] = \
- Selector(
- _('Profile'),
- lambda preset: self._select_profile(preset),
- display_func=lambda x: x if x else 'None'
- )
- self._menu_options['audio'] = \
- Selector(
- _('Audio'),
- lambda preset: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)),preset),
- display_func=lambda x: x if x else 'None',
- default=None
- )
-
- self._menu_options['parallel downloads'] = \
- Selector(
- _('Parallel Downloads'),
- add_number_of_parrallel_downloads,
- display_func=lambda x: x if x else '0',
- default=0
- )
-
- self._menu_options['kernels'] = \
- Selector(
- _('Kernels'),
- lambda preset: select_kernel(preset),
- default=['linux'])
- self._menu_options['packages'] = \
- Selector(
- _('Additional packages'),
- # lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)),
- ask_additional_packages_to_install,
- default=[])
- self._menu_options['additional-repositories'] = \
- Selector(
- _('Optional repositories'),
- select_additional_repositories,
- default=[])
- self._menu_options['nic'] = \
- Selector(
- _('Network configuration'),
- ask_to_configure_network,
- display_func=lambda x: self._display_network_conf(x),
- preview_func=self._prev_network_config,
- default={})
- self._menu_options['timezone'] = \
- Selector(
- _('Timezone'),
- lambda preset: ask_for_a_timezone(preset),
- default='UTC')
- self._menu_options['ntp'] = \
- Selector(
- _('Automatic time sync (NTP)'),
- lambda preset: self._select_ntp(preset),
- default=True)
- self._menu_options['__separator__'] = \
- Selector('')
- self._menu_options['save_config'] = \
- Selector(
- _('Save configuration'),
- lambda preset: save_config(self._data_store),
- no_store=True)
- self._menu_options['install'] = \
- Selector(
- self._install_text(),
- exec_func=lambda n,v: True if len(self._missing_configs()) == 0 else False,
- preview_func=self._prev_install_missing_config,
- no_store=True)
-
- self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1))
-
- def _update_install_text(self, name :Optional[str] = None, result :Any = None):
- text = self._install_text()
- self._menu_options['install'].update_description(text)
-
- def post_callback(self,name :Optional[str] = None ,result :Any = None):
- self._update_install_text(name, result)
-
- def _install_text(self):
- missing = len(self._missing_configs())
- if missing > 0:
- return _('Install ({} config(s) missing)').format(missing)
- return _('Install')
-
- def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str:
- if not cur_value:
- return _('Not configured, unavailable unless setup manually')
- else:
- if isinstance(cur_value, list):
- return str(_('Configured {} interfaces')).format(len(cur_value))
- else:
- return str(cur_value)
-
- def _disk_encryption(self, preset: Optional[DiskEncryption]) -> Optional[DiskEncryption]:
- data_store: Dict[str, Any] = {}
-
- selector = self._menu_options['disk_layouts']
-
- if selector.has_selection():
- layouts: Dict[str, Dict[str, Any]] = selector.current_selection
- else:
- # this should not happen as the encryption menu has the disk layout as dependency
- raise ValueError('No disk layout specified')
-
- disk_encryption = DiskEncryptionMenu(data_store, preset, layouts).run()
- return disk_encryption
-
- def _prev_network_config(self) -> Optional[str]:
- selector = self._menu_options['nic']
- if selector.has_selection():
- ifaces = selector.current_selection
- if isinstance(ifaces, list):
- return FormattedOutput.as_table(ifaces)
- return None
-
- def _prev_harddrives(self) -> Optional[str]:
- selector = self._menu_options['harddrives']
- if selector.has_selection():
- drives = selector.current_selection
- return FormattedOutput.as_table(drives)
- return None
-
- def _prev_disk_layouts(self) -> Optional[str]:
- selector = self._menu_options['disk_layouts']
- if selector.has_selection():
- layouts: Dict[str, Dict[str, Any]] = selector.current_selection
-
- output = ''
- for device, layout in layouts.items():
- output += f'{_("Device")}: {device}\n\n'
- output += current_partition_layout(layout['partitions'], with_title=False)
- output += '\n\n'
-
- return output.rstrip()
-
- return None
-
- def _display_disk_layout(self, current_value: Optional[Dict[str, Any]]) -> str:
- if current_value:
- total_partitions = [entry['partitions'] for entry in current_value.values()]
- total_nr = sum([len(p) for p in total_partitions])
- return f'{total_nr} {_("Partitions")}'
- return ''
-
- def _prev_disk_encryption(self) -> Optional[str]:
- selector = self._menu_options['disk_encryption']
- if selector.has_selection():
- encryption: DiskEncryption = selector.current_selection
-
- enc_type = EncryptionType.type_to_text(encryption.encryption_type)
- output = str(_('Encryption type')) + f': {enc_type}\n'
- output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n'
-
- if encryption.all_partitions:
- output += 'Partitions: {} selected'.format(len(encryption.all_partitions)) + '\n'
-
- if encryption.hsm_device:
- output += f'HSM: {encryption.hsm_device.manufacturer}'
-
- return output
-
- return None
-
- def _display_disk_encryption(self, current_value: Optional[DiskEncryption]) -> str:
- if current_value:
- return EncryptionType.type_to_text(current_value.encryption_type)
- return ''
-
- def _prev_install_missing_config(self) -> Optional[str]:
- if missing := self._missing_configs():
- text = str(_('Missing configurations:\n'))
- for m in missing:
- text += f'- {m}\n'
- return text[:-1] # remove last new line
- return None
-
- def _prev_users(self) -> Optional[str]:
- selector = self._menu_options['!users']
- if selector.has_selection():
- users: List[User] = selector.current_selection
- return FormattedOutput.as_table(users)
- return None
-
- def _missing_configs(self) -> List[str]:
- def check(s):
- return self._menu_options.get(s).has_selection()
-
- def has_superuser() -> bool:
- users = self._menu_options['!users'].current_selection
- return any([u.sudo for u in users])
-
- missing = []
- if not check('bootloader'):
- missing += ['Bootloader']
- if not check('hostname'):
- missing += ['Hostname']
- if not check('!root-password') and not has_superuser():
- missing += [str(_('Either root-password or at least 1 user with sudo privileges must be specified'))]
- if self._disk_check:
- if not check('harddrives'):
- missing += [str(_('Drive(s)'))]
- if check('harddrives'):
- if not self._menu_options['harddrives'].is_empty() and not check('disk_layouts'):
- missing += [str(_('Disk layout'))]
-
- return missing
-
- def _set_root_password(self) -> Optional[str]:
- prompt = str(_('Enter root password (leave blank to disable root): '))
- password = get_password(prompt=prompt)
- return password
-
- # def _select_encrypted_password(self) -> Optional[str]:
- # if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
- # return passwd
- # return None
-
- def _select_ntp(self, preset :bool = True) -> bool:
- ntp = ask_ntp(preset)
-
- value = str(ntp).lower()
- SysCommand(f'timedatectl set-ntp {value}')
-
- return ntp
-
- def _select_harddrives(self, old_harddrives: List[str] = []) -> List:
- harddrives = select_harddrives(old_harddrives)
-
- if harddrives is not None:
- if len(harddrives) == 0:
- prompt = _(
- "You decided to skip harddrive selection\nand will use whatever drive-setup is mounted at {} (experimental)\n"
- "WARNING: Archinstall won't check the suitability of this setup\n"
- "Do you wish to continue?"
- ).format(storage['MOUNT_POINT'])
-
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), skip=False).run()
-
- if choice.value == Menu.no():
- self._disk_check = True
- return self._select_harddrives(old_harddrives)
- else:
- self._disk_check = False
-
- # in case the harddrives got changed we have to reset the disk layout as well
- if old_harddrives != harddrives:
- self._menu_options['disk_layouts'].set_current_selection(None)
- storage['arguments']['disk_layouts'] = {}
-
- return harddrives
-
- def _select_profile(self, preset) -> Optional[Profile]:
- ret: Optional[Profile] = None
- profile = select_profile(preset)
-
- if profile is None:
- if any([
- archinstall.storage.get('profile_minimal', False),
- archinstall.storage.get('_selected_servers', None),
- archinstall.storage.get('_desktop_profile', None),
- archinstall.arguments.get('desktop-environment', None),
- archinstall.arguments.get('gfx_driver_packages', None)
- ]):
- return preset
- else: # ctrl+c was actioned and all profile settings have been reset
- return None
-
- servers = archinstall.storage.get('_selected_servers', [])
- desktop = archinstall.storage.get('_desktop_profile', None)
- desktop_env = archinstall.arguments.get('desktop-environment', None)
- gfx_driver = archinstall.arguments.get('gfx_driver_packages', None)
-
- # Check the potentially selected profiles preparations to get early checks if some additional questions are needed.
- if profile and profile.has_prep_function():
- namespace = f'{profile.namespace}.py'
- with profile.load_instructions(namespace=namespace) as imported:
- if imported._prep_function(servers=servers, desktop=desktop, desktop_env=desktop_env, gfx_driver=gfx_driver):
- ret = profile
-
- match ret.name:
- case 'minimal':
- reset = ['_selected_servers', '_desktop_profile', 'desktop-environment', 'gfx_driver_packages']
- case 'server':
- reset = ['_desktop_profile', 'desktop-environment']
- case 'desktop':
- reset = ['_selected_servers']
- case 'xorg':
- reset = ['_selected_servers', '_desktop_profile', 'desktop-environment']
-
- for r in reset:
- archinstall.storage[r] = None
- else:
- return self._select_profile(preset)
- elif profile:
- ret = profile
-
- return ret
-
- def _create_user_account(self, defined_users: List[User]) -> List[User]:
- users = ask_for_additional_users(defined_users=defined_users)
- return users
diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py
index 1e09d987..de18791c 100644
--- a/archinstall/lib/menu/list_manager.py
+++ b/archinstall/lib/menu/list_manager.py
@@ -3,6 +3,7 @@ from os import system
from typing import Any, TYPE_CHECKING, Dict, Optional, Tuple, List
from .menu import Menu
+from ..output import FormattedOutput
if TYPE_CHECKING:
_: Any
@@ -34,7 +35,7 @@ class ListManager:
self._data = copy.deepcopy(entries)
explainer = str(_('\n Choose an object from the list, and select one of the available actions for it to execute'))
- self._prompt = prompt + explainer if prompt else explainer
+ self._prompt = prompt if prompt else explainer
self._separator = ''
self._confirm_action = str(_('Confirm and exit'))
@@ -44,13 +45,18 @@ class ListManager:
self._base_actions = base_actions
self._sub_menu_actions = sub_menu_actions
- self._last_choice = None
+ self._last_choice: Optional[str] = None
@property
- def last_choice(self):
+ def last_choice(self) -> Optional[str]:
return self._last_choice
- def run(self):
+ def is_last_choice_cancel(self) -> bool:
+ if self._last_choice is not None:
+ return self._last_choice == self._cancel_action
+ return False
+
+ def run(self) -> List[Any]:
while True:
# this will return a dictionary with the key as the menu entry to be displayed
# and the value is the original value from the self._data container
@@ -75,11 +81,12 @@ class ListManager:
self._data = self.handle_action(choice.value, None, self._data)
elif choice.value in self._terminate_actions:
break
- else: # an entry of the existing selection was choosen
- selected_entry = data_formatted[choice.value]
+ else: # an entry of the existing selection was chosen
+ selected_entry = data_formatted[choice.value] # type: ignore
self._run_actions_on_entry(selected_entry)
- self._last_choice = choice
+ self._last_choice = choice.value # type: ignore
+
if choice.value == self._cancel_action:
return self._original_data # return the original list
else:
@@ -121,22 +128,41 @@ class ListManager:
if choice.value and choice.value != self._cancel_action:
self._data = self.handle_action(choice.value, entry, self._data)
- def selected_action_display(self, selection: Any) -> str:
- # this will return the value to be displayed in the
- # "Select an action for '{}'" string
- raise NotImplementedError('Please implement me in the child class')
+ def reformat(self, data: List[Any]) -> Dict[str, Optional[Any]]:
+ """
+ Default implementation of the table to be displayed.
+ Override if any custom formatting is needed
+ """
+ table = FormattedOutput.as_table(data)
+ rows = table.split('\n')
+
+ # these are the header rows of the table and do not map to any User obviously
+ # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
+ # the selectable rows so the header has to be aligned
+ display_data: Dict[str, Optional[Any]] = {f' {rows[0]}': None, f' {rows[1]}': None}
+
+ for row, entry in zip(rows[2:], data):
+ row = row.replace('|', '\\|')
+ display_data[row] = entry
- def reformat(self, data: List[Any]) -> Dict[str, Any]:
- # this should return a dictionary of display string to actual data entry
- # mapping; if the value for a given display string is None it will be used
- # in the header value (useful when displaying tables)
+ return display_data
+
+ def selected_action_display(self, selection: Any) -> str:
+ """
+ this will return the value to be displayed in the
+ "Select an action for '{}'" string
+ """
raise NotImplementedError('Please implement me in the child class')
def handle_action(self, action: Any, entry: Optional[Any], data: List[Any]) -> List[Any]:
- # this function is called when a base action or
- # a specific action for an entry is triggered
+ """
+ this function is called when a base action or
+ a specific action for an entry is triggered
+ """
raise NotImplementedError('Please implement me in the child class')
- def filter_options(self, selection :Any, options :List[str]) -> List[str]:
- # filter which actions to show for an specific selection
+ def filter_options(self, selection: Any, options: List[str]) -> List[str]:
+ """
+ filter which actions to show for an specific selection
+ """
return options
diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py
index 09685c55..38301d3a 100644
--- a/archinstall/lib/menu/menu.py
+++ b/archinstall/lib/menu/menu.py
@@ -3,14 +3,11 @@ from enum import Enum, auto
from os import system
from typing import Dict, List, Union, Any, TYPE_CHECKING, Optional, Callable
-from .simple_menu import TerminalMenu
+from simple_term_menu import TerminalMenu # type: ignore
from ..exceptions import RequirementError
-from ..output import log
+from ..output import debug
-from collections.abc import Iterable
-import sys
-import logging
if TYPE_CHECKING:
_: Any
@@ -27,42 +24,61 @@ class MenuSelection:
type_: MenuSelectionType
value: Optional[Union[str, List[str]]] = None
+ @property
+ def single_value(self) -> Any:
+ return self.value # type: ignore
+
+ @property
+ def multi_value(self) -> List[Any]:
+ return self.value # type: ignore
+
class Menu(TerminalMenu):
+ _menu_is_active: bool = False
+
+ @staticmethod
+ def is_menu_active() -> bool:
+ return Menu._menu_is_active
+
+ @classmethod
+ def back(cls) -> str:
+ return str(_('↠Back'))
@classmethod
- def yes(cls):
+ def yes(cls) -> str:
return str(_('yes'))
@classmethod
- def no(cls):
+ def no(cls) -> str:
return str(_('no'))
@classmethod
- def yes_no(cls):
+ def yes_no(cls) -> List[str]:
return [cls.yes(), cls.no()]
def __init__(
self,
- title :str,
- p_options :Union[List[str], Dict[str, Any]],
- skip :bool = True,
- multi :bool = False,
- default_option : Optional[str] = None,
- sort :bool = True,
- preset_values :Union[str, List[str]] = None,
- cursor_index : Optional[int] = None,
- preview_command: Optional[Callable] = None,
+ title: str,
+ p_options: Union[List[str], Dict[str, Any]],
+ skip: bool = True,
+ multi: bool = False,
+ default_option: Optional[str] = None,
+ sort: bool = True,
+ preset_values: Optional[Union[str, List[str]]] = None,
+ cursor_index: Optional[int] = None,
+ preview_command: Optional[Callable[[Any], str | None]] = None,
preview_size: float = 0.0,
preview_title: str = 'Info',
- header :Union[List[str],str] = None,
- allow_reset :bool = False,
- allow_reset_warning_msg :str = '',
+ header: Union[List[str], str] = [],
+ allow_reset: bool = False,
+ allow_reset_warning_msg: Optional[str] = None,
clear_screen: bool = True,
show_search_hint: bool = True,
cycle_cursor: bool = True,
clear_menu_on_exit: bool = True,
- skip_empty_entries: bool = False
+ skip_empty_entries: bool = False,
+ display_back_option: bool = False,
+ extra_bottom_space: bool = False
):
"""
Creates a new menu
@@ -72,7 +88,7 @@ class Menu(TerminalMenu):
:param p_options: Options to be displayed in the menu to chose from;
if dict is specified then the keys of such will be used as options
- :type options: list, dict
+ :type p_options: list, dict
:param skip: Indicate if the selection is not mandatory and can be skipped
:type skip: bool
@@ -101,46 +117,27 @@ class Menu(TerminalMenu):
:param preview_title: Title of the preview window
:type preview_title: str
- param header: one or more header lines for the menu
- type param: string or list
+ :param header: one or more header lines for the menu
+ :type header: string or list
- param raise_error_on_interrupt: This will explicitly handle a ctrl+c instead and return that specific state
- type param: bool
+ :param allow_reset: This will explicitly handle a ctrl+c instead and return that specific state
+ :type allow_reset: bool
- param raise_error_warning_msg: If raise_error_on_interrupt is True and this is non-empty, there will be a warning with a user confirmation displayed
- type param: str
+ param allow_reset_warning_msg: If raise_error_on_interrupt is True the warning is set, a user confirmation is displayed
+ type allow_reset_warning_msg: str
- :param kwargs : any SimpleTerminal parameter
+ :param extra_bottom_space: Add an extra empty line at the end of the menu
+ :type extra_bottom_space: bool
"""
- # we guarantee the inmutability of the options outside the class.
- # an unknown number of iterables (.keys(),.values(),generator,...) can't be directly copied, in this case
- # we recourse to make them lists before, but thru an exceptions
- # this is the old code, which is not maintenable with more types
- # options = copy(list(p_options) if isinstance(p_options,(type({}.keys()),type({}.values()))) else p_options)
- # We check that the options are iterable. If not we abort. Else we copy them to lists
- # it options is a dictionary we use the values as entries of the list
- # if options is a string object, each character becomes an entry
- # if options is a list, we implictily build a copy to maintain immutability
- if not isinstance(p_options,Iterable):
- log(f"Objects of type {type(p_options)} is not iterable, and are not supported at Menu",fg="red")
- log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING)
- raise RequirementError("Menu() requires an iterable as option.")
-
- self._default_str = str(_('(default)'))
-
- if isinstance(p_options,dict):
+ if isinstance(p_options, Dict):
options = list(p_options.keys())
else:
options = list(p_options)
if not options:
- log(" * Menu didn't find any options to choose from * ", fg='red')
- log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING)
raise RequirementError('Menu.__init__() requires at least one option to proceed.')
if any([o for o in options if not isinstance(o, str)]):
- log(" * Menu options must be of type string * ", fg='red')
- log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING)
raise RequirementError('Menu.__init__() requires the options to be of type string')
if sort:
@@ -152,7 +149,6 @@ class Menu(TerminalMenu):
self._multi = multi
self._raise_error_on_interrupt = allow_reset
self._raise_error_warning_msg = allow_reset_warning_msg
- self._preview_command = preview_command
action_info = ''
if skip:
@@ -179,10 +175,28 @@ class Menu(TerminalMenu):
if default_option:
# if a default value was specified we move that one
# to the top of the list and mark it as default as well
- default = f'{default_option} {self._default_str}'
- self._menu_options = [default] + [o for o in self._menu_options if default_option != o]
+ self._menu_options = [self._default_menu_value] + [o for o in self._menu_options if default_option != o]
+
+ if display_back_option and not multi and skip:
+ skip_empty_entries = True
+ self._menu_options += ['', self.back()]
+
+ if extra_bottom_space:
+ skip_empty_entries = True
+ self._menu_options += ['']
+
+ preset_list: Optional[List[str]] = None
+
+ if preset_values and isinstance(preset_values, str):
+ preset_list = [preset_values]
- self._preselection(preset_values,cursor_index)
+ calc_cursor_idx = self._determine_cursor_pos(preset_list, cursor_index)
+
+ # when we're not in multi selection mode we don't care about
+ # passing the pre-selection list to the menu as the position
+ # of the cursor is the one determining the pre-selection
+ if not self._multi:
+ preset_values = None
cursor = "> "
main_menu_cursor_style = ("fg_cyan", "bold")
@@ -194,13 +208,10 @@ class Menu(TerminalMenu):
menu_cursor=cursor,
menu_cursor_style=main_menu_cursor_style,
menu_highlight_style=main_menu_style,
- # cycle_cursor=True,
- # clear_screen=True,
multi_select=multi,
- # show_search_hint=True,
- preselected_entries=self.preset_values,
- cursor_index=self.cursor_index,
- preview_command=lambda x: self._preview_wrapper(preview_command, x),
+ preselected_entries=preset_values,
+ cursor_index=calc_cursor_idx,
+ preview_command=lambda x: self._show_preview(preview_command, x),
preview_size=preview_size,
preview_title=preview_title,
raise_error_on_interrupt=self._raise_error_on_interrupt,
@@ -212,6 +223,28 @@ class Menu(TerminalMenu):
skip_empty_entries=skip_empty_entries
)
+ @property
+ def _default_menu_value(self) -> str:
+ default_str = str(_('(default)'))
+ return f'{self._default_option} {default_str}'
+
+ def _show_preview(
+ self,
+ preview_command: Optional[Callable[[Any], str | None]],
+ selection: str
+ ) -> Optional[str]:
+ if selection == self.back():
+ return None
+
+ if preview_command:
+ if self._default_option is not None and self._default_menu_value == selection:
+ selection = self._default_option
+
+ if res := preview_command(selection):
+ return res.rstrip('\n')
+
+ return None
+
def _show(self) -> MenuSelection:
try:
idx = self.show()
@@ -219,45 +252,47 @@ class Menu(TerminalMenu):
return MenuSelection(type_=MenuSelectionType.Reset)
def check_default(elem):
- if self._default_option is not None and f'{self._default_option} {self._default_str}' in elem:
+ if self._default_option is not None and self._default_menu_value in elem:
return self._default_option
else:
return elem
if idx is not None:
- if isinstance(idx, (list, tuple)):
+ if isinstance(idx, (list, tuple)): # on multi selection
results = []
for i in idx:
option = check_default(self._menu_options[i])
results.append(option)
return MenuSelection(type_=MenuSelectionType.Selection, value=results)
- else:
+ else: # on single selection
result = check_default(self._menu_options[idx])
return MenuSelection(type_=MenuSelectionType.Selection, value=result)
else:
return MenuSelection(type_=MenuSelectionType.Skip)
- def _preview_wrapper(self, preview_command: Optional[Callable], current_selection: str) -> Optional[str]:
- if preview_command:
- if self._default_option is not None and f'{self._default_option} {self._default_str}' == current_selection:
- current_selection = self._default_option
- return preview_command(current_selection)
- return None
-
def run(self) -> MenuSelection:
- ret = self._show()
+ Menu._menu_is_active = True
+
+ selection = self._show()
- if ret.type_ == MenuSelectionType.Reset:
- if self._raise_error_on_interrupt and len(self._raise_error_warning_msg) > 0:
+ if selection.type_ == MenuSelectionType.Reset:
+ if self._raise_error_on_interrupt and self._raise_error_warning_msg is not None:
response = Menu(self._raise_error_warning_msg, Menu.yes_no(), skip=False).run()
if response.value == Menu.no():
return self.run()
- elif ret.type_ is MenuSelectionType.Skip:
+ elif selection.type_ is MenuSelectionType.Skip:
if not self._skip:
system('clear')
return self.run()
- return ret
+ if selection.type_ == MenuSelectionType.Selection:
+ if selection.value == self.back():
+ selection.type_ = MenuSelectionType.Skip
+ selection.value = None
+
+ Menu._menu_is_active = False
+
+ return selection
def set_cursor_pos(self,pos :int):
if pos and 0 < pos < len(self._menu_entries):
@@ -269,31 +304,47 @@ class Menu(TerminalMenu):
pos = self._menu_entries.index(value)
self.set_cursor_pos(pos)
- def _preselection(self,preset_values :Union[str, List[str]] = [], cursor_index : Optional[int] = None):
- def from_preset_to_cursor():
- if preset_values:
- # if the value is not extant return 0 as cursor index
+ def _determine_cursor_pos(
+ self,
+ preset: Optional[List[str]] = None,
+ cursor_index: Optional[int] = None
+ ) -> Optional[int]:
+ """
+ The priority order to determine the cursor position is:
+ 1. A static cursor position was provided
+ 2. Preset values have been provided so the cursor will be
+ positioned on those
+ 3. A default value for a selection is given so the cursor
+ will be placed on such
+ """
+ if cursor_index:
+ return cursor_index
+
+ if preset:
+ indexes = []
+
+ for p in preset:
try:
- if isinstance(preset_values,str):
- self.cursor_index = self._menu_options.index(self.preset_values)
- else: # should return an error, but this is smoother
- self.cursor_index = self._menu_options.index(self.preset_values[0])
- except ValueError:
- self.cursor_index = 0
-
- self.cursor_index = cursor_index
- if not preset_values:
- self.preset_values = None
- return
-
- self.preset_values = preset_values
+ # the options of the table selection menu
+ # are already escaped so we have to escape
+ # the preset values as well for the comparison
+ if '|' in p:
+ p = p.replace('|', '\\|')
+
+ if p in self._menu_options:
+ idx = self._menu_options.index(p)
+ else:
+ idx = self._menu_options.index(self._default_menu_value)
+ indexes.append(idx)
+ except (IndexError, ValueError):
+ debug(f'Error finding index of {p}: {self._menu_options}')
+
+ if len(indexes) == 0:
+ indexes.append(0)
+
+ return indexes[0]
+
if self._default_option:
- if isinstance(preset_values,str) and self._default_option == preset_values:
- self.preset_values = f"{preset_values} {self._default_str}"
- elif isinstance(preset_values,(list,tuple)) and self._default_option in preset_values:
- idx = preset_values.index(self._default_option)
- self.preset_values[idx] = f"{preset_values[idx]} {self._default_str}"
- if cursor_index is None or not self._multi:
- from_preset_to_cursor()
- if not self._multi: # Not supported by the infraestructure
- self.preset_values = None
+ return self._menu_options.index(self._default_menu_value)
+
+ return None
diff --git a/archinstall/lib/menu/simple_menu.py b/archinstall/lib/menu/simple_menu.py
deleted file mode 100644
index 1980e2ce..00000000
--- a/archinstall/lib/menu/simple_menu.py
+++ /dev/null
@@ -1,2002 +0,0 @@
-"""
-This file is copied over from the simple-term-menu project
-(https://github.com/IngoMeyer441/simple-term-menu)
-In order to comply with installation methods of Arch Linux.
-We here by copy the MIT license attached to the project at the time of copy:
-
-Copyright 2021 Forschungszentrum Jülich GmbH
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-"""
-import argparse
-import copy
-import ctypes
-import io
-import locale
-import os
-import platform
-import re
-import shlex
-import signal
-import string
-import subprocess
-import sys
-from locale import getlocale
-from types import FrameType
-from typing import (
- Any,
- Callable,
- Dict,
- Iterable,
- Iterator,
- List,
- Match,
- Optional,
- Pattern,
- Sequence,
- Set,
- TextIO,
- Tuple,
- Union,
- cast,
-)
-
-try:
- import termios
-except ImportError as e:
- raise NotImplementedError('"{}" is currently not supported.'.format(platform.system())) from e
-
-__author__ = "Ingo Meyer"
-__email__ = "i.meyer@fz-juelich.de"
-__copyright__ = "Copyright © 2021 Forschungszentrum Jülich GmbH. All rights reserved."
-__license__ = "MIT"
-__version_info__ = (1, 5, 0)
-__version__ = ".".join(map(str, __version_info__))
-
-
-DEFAULT_ACCEPT_KEYS = ("enter",)
-DEFAULT_CLEAR_MENU_ON_EXIT = True
-DEFAULT_CLEAR_SCREEN = False
-DEFAULT_CYCLE_CURSOR = True
-DEFAULT_EXIT_ON_SHORTCUT = True
-DEFAULT_MENU_CURSOR = "> "
-DEFAULT_MENU_CURSOR_STYLE = ("fg_red", "bold")
-DEFAULT_MENU_HIGHLIGHT_STYLE = ("standout",)
-DEFAULT_MULTI_SELECT = False
-DEFAULT_MULTI_SELECT_CURSOR = "[*] "
-DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE = ("fg_gray",)
-DEFAULT_MULTI_SELECT_CURSOR_STYLE = ("fg_yellow", "bold")
-DEFAULT_MULTI_SELECT_KEYS = (" ", "tab")
-DEFAULT_MULTI_SELECT_SELECT_ON_ACCEPT = True
-DEFAULT_PREVIEW_BORDER = True
-DEFAULT_PREVIEW_SIZE = 0.25
-DEFAULT_PREVIEW_TITLE = "preview"
-DEFAULT_QUIT_KEYS = ("escape", "q")
-DEFAULT_SEARCH_CASE_SENSITIVE = False
-DEFAULT_SEARCH_HIGHLIGHT_STYLE = ("fg_black", "bg_yellow", "bold")
-DEFAULT_SEARCH_KEY = "/"
-DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE = ("fg_gray",)
-DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE = ("fg_blue",)
-DEFAULT_SHOW_MULTI_SELECT_HINT = False
-DEFAULT_SHOW_SEARCH_HINT = False
-DEFAULT_SHOW_SHORTCUT_HINTS = False
-DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR = True
-DEFAULT_STATUS_BAR_BELOW_PREVIEW = False
-DEFAULT_STATUS_BAR_STYLE = ("fg_yellow", "bg_black")
-MIN_VISIBLE_MENU_ENTRIES_COUNT = 3
-
-
-class InvalidParameterCombinationError(Exception):
- pass
-
-
-class InvalidStyleError(Exception):
- pass
-
-
-class NoMenuEntriesError(Exception):
- pass
-
-
-class PreviewCommandFailedError(Exception):
- pass
-
-
-class UnknownMenuEntryError(Exception):
- pass
-
-
-def get_locale() -> str:
- user_locale = locale.getlocale()[1]
- if user_locale is None:
- return "ascii"
- else:
- return user_locale.lower()
-
-
-def wcswidth(text: str) -> int:
- if not hasattr(wcswidth, "libc"):
- if platform.system() == "Darwin":
- wcswidth.libc = ctypes.cdll.LoadLibrary("libSystem.dylib") # type: ignore
- else:
- wcswidth.libc = ctypes.cdll.LoadLibrary("libc.so.6") # type: ignore
- user_locale = get_locale()
- # First replace any null characters with the unicode replacement character (U+FFFD) since they cannot be passed
- # in a `c_wchar_p`
- encoded_text = text.replace("\0", "\uFFFD").encode(encoding=user_locale, errors="replace")
- return wcswidth.libc.wcswidth( # type: ignore
- ctypes.c_wchar_p(encoded_text.decode(encoding=user_locale)), len(encoded_text)
- )
-
-
-def static_variables(**variables: Any) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
- def decorator(f: Callable[..., Any]) -> Callable[..., Any]:
- for key, value in variables.items():
- setattr(f, key, value)
- return f
-
- return decorator
-
-
-class BoxDrawingCharacters:
- if getlocale()[1] == "UTF-8":
- # Unicode box characters
- horizontal = "─"
- vertical = "│"
- upper_left = "┌"
- upper_right = "â”"
- lower_left = "â””"
- lower_right = "┘"
- else:
- # ASCII box characters
- horizontal = "-"
- vertical = "|"
- upper_left = "+"
- upper_right = "+"
- lower_left = "+"
- lower_right = "+"
-
-
-class TerminalMenu:
- class Search:
- def __init__(
- self,
- menu_entries: Iterable[str],
- search_text: Optional[str] = None,
- case_senitive: bool = False,
- show_search_hint: bool = False,
- ):
- self._menu_entries = menu_entries
- self._case_sensitive = case_senitive
- self._show_search_hint = show_search_hint
- self._matches = [] # type: List[Tuple[int, Match[str]]]
- self._search_regex = None # type: Optional[Pattern[str]]
- self._change_callback = None # type: Optional[Callable[[], None]]
- # Use the property setter since it has some more logic
- self.search_text = search_text
-
- def _update_matches(self) -> None:
- if self._search_regex is None:
- self._matches = []
- else:
- matches = []
- for i, menu_entry in enumerate(self._menu_entries):
- match_obj = self._search_regex.search(menu_entry)
- if match_obj:
- matches.append((i, match_obj))
- self._matches = matches
-
- @property
- def matches(self) -> List[Tuple[int, Match[str]]]:
- return list(self._matches)
-
- @property
- def search_regex(self) -> Optional[Pattern[str]]:
- return self._search_regex
-
- @property
- def search_text(self) -> Optional[str]:
- return self._search_text
-
- @search_text.setter
- def search_text(self, text: Optional[str]) -> None:
- self._search_text = text
- search_text = self._search_text
- self._search_regex = None
- while search_text and self._search_regex is None:
- try:
- self._search_regex = re.compile(search_text, flags=re.IGNORECASE if not self._case_sensitive else 0)
- except re.error:
- search_text = search_text[:-1]
- self._update_matches()
- if self._change_callback:
- self._change_callback()
-
- @property
- def change_callback(self) -> Optional[Callable[[], None]]:
- return self._change_callback
-
- @change_callback.setter
- def change_callback(self, callback: Optional[Callable[[], None]]) -> None:
- self._change_callback = callback
-
- @property
- def occupied_lines_count(self) -> int:
- if not self and not self._show_search_hint:
- return 0
- else:
- return 1
-
- def __bool__(self) -> bool:
- return self._search_text is not None
-
- def __contains__(self, menu_index: int) -> bool:
- return any(i == menu_index for i, _ in self._matches)
-
- def __len__(self) -> int:
- return wcswidth(self._search_text) if self._search_text is not None else 0
-
- class Selection:
- def __init__(self, num_menu_entries: int, preselected_indices: Optional[Iterable[int]] = None):
- self._num_menu_entries = num_menu_entries
- self._selected_menu_indices = set(preselected_indices) if preselected_indices is not None else set()
-
- def clear(self) -> None:
- self._selected_menu_indices.clear()
-
- def add(self, menu_index: int) -> None:
- self[menu_index] = True
-
- def remove(self, menu_index: int) -> None:
- self[menu_index] = False
-
- def toggle(self, menu_index: int) -> bool:
- self[menu_index] = menu_index not in self._selected_menu_indices
- return self[menu_index]
-
- def __bool__(self) -> bool:
- return bool(self._selected_menu_indices)
-
- def __contains__(self, menu_index: int) -> bool:
- return menu_index in self._selected_menu_indices
-
- def __getitem__(self, menu_index: int) -> bool:
- return menu_index in self._selected_menu_indices
-
- def __setitem__(self, menu_index: int, is_selected: bool) -> None:
- if is_selected:
- self._selected_menu_indices.add(menu_index)
- else:
- self._selected_menu_indices.remove(menu_index)
-
- def __iter__(self) -> Iterator[int]:
- return iter(self._selected_menu_indices)
-
- @property
- def selected_menu_indices(self) -> Tuple[int, ...]:
- return tuple(sorted(self._selected_menu_indices))
-
- class View:
- def __init__(
- self,
- menu_entries: Iterable[str],
- search: "TerminalMenu.Search",
- selection: "TerminalMenu.Selection",
- viewport: "TerminalMenu.Viewport",
- cycle_cursor: bool = True,
- skip_indices: List[int] = [],
- ):
- self._menu_entries = list(menu_entries)
- self._search = search
- self._selection = selection
- self._viewport = viewport
- self._cycle_cursor = cycle_cursor
- self._active_displayed_index = None # type: Optional[int]
- self._skip_indices = skip_indices
- self.update_view()
-
- def update_view(self) -> None:
- if self._search and self._search.search_text != "":
- self._displayed_index_to_menu_index = tuple(i for i, match_obj in self._search.matches)
- else:
- self._displayed_index_to_menu_index = tuple(range(len(self._menu_entries)))
- self._menu_index_to_displayed_index = {
- menu_index: displayed_index
- for displayed_index, menu_index in enumerate(self._displayed_index_to_menu_index)
- }
- self._active_displayed_index = 0 if self._displayed_index_to_menu_index else None
- self._viewport.search_lines_count = self._search.occupied_lines_count
- self._viewport.keep_visible(self._active_displayed_index)
-
- def increment_active_index(self) -> None:
- if self._active_displayed_index is not None:
- if self._active_displayed_index + 1 < len(self._displayed_index_to_menu_index):
- self._active_displayed_index += 1
- elif self._cycle_cursor:
- self._active_displayed_index = 0
- self._viewport.keep_visible(self._active_displayed_index)
-
- if self._active_displayed_index in self._skip_indices:
- self.increment_active_index()
-
- def decrement_active_index(self) -> None:
- if self._active_displayed_index is not None:
- if self._active_displayed_index > 0:
- self._active_displayed_index -= 1
- elif self._cycle_cursor:
- self._active_displayed_index = len(self._displayed_index_to_menu_index) - 1
- self._viewport.keep_visible(self._active_displayed_index)
-
- if self._active_displayed_index in self._skip_indices:
- self.decrement_active_index()
-
- def is_visible(self, menu_index: int) -> bool:
- return menu_index in self._menu_index_to_displayed_index and (
- self._viewport.lower_index
- <= self._menu_index_to_displayed_index[menu_index]
- <= self._viewport.upper_index
- )
-
- def convert_menu_index_to_displayed_index(self, menu_index: int) -> Optional[int]:
- if menu_index in self._menu_index_to_displayed_index:
- return self._menu_index_to_displayed_index[menu_index]
- else:
- return None
-
- def convert_displayed_index_to_menu_index(self, displayed_index: int) -> int:
- return self._displayed_index_to_menu_index[displayed_index]
-
- @property
- def active_menu_index(self) -> Optional[int]:
- if self._active_displayed_index is not None:
- return self._displayed_index_to_menu_index[self._active_displayed_index]
- else:
- return None
-
- @active_menu_index.setter
- def active_menu_index(self, value: int) -> None:
- self._selected_index = value
- self._active_displayed_index = [
- displayed_index
- for displayed_index, menu_index in enumerate(self._displayed_index_to_menu_index)
- if menu_index == value
- ][0]
- self._viewport.keep_visible(self._active_displayed_index)
-
- @property
- def active_displayed_index(self) -> Optional[int]:
- return self._active_displayed_index
-
- @property
- def displayed_selected_indices(self) -> List[int]:
- return [
- self._menu_index_to_displayed_index[selected_index]
- for selected_index in self._selection
- if selected_index in self._menu_index_to_displayed_index
- ]
-
- def __bool__(self) -> bool:
- return self._active_displayed_index is not None
-
- def __iter__(self) -> Iterator[Tuple[int, int, str]]:
- for displayed_index, menu_index in enumerate(self._displayed_index_to_menu_index):
- if self._viewport.lower_index <= displayed_index <= self._viewport.upper_index:
- yield (displayed_index, menu_index, self._menu_entries[menu_index])
-
- class Viewport:
- def __init__(
- self,
- num_menu_entries: int,
- title_lines_count: int,
- status_bar_lines_count: int,
- preview_lines_count: int,
- search_lines_count: int,
- ):
- self._num_menu_entries = num_menu_entries
- self._title_lines_count = title_lines_count
- self._status_bar_lines_count = status_bar_lines_count
- # Use the property setter since it has some more logic
- self.preview_lines_count = preview_lines_count
- self.search_lines_count = search_lines_count
- self._num_lines = self._calculate_num_lines()
- self._viewport = (0, min(self._num_menu_entries, self._num_lines) - 1)
- self.keep_visible(cursor_position=None, refresh_terminal_size=False)
-
- def _calculate_num_lines(self) -> int:
- return (
- TerminalMenu._num_lines()
- - self._title_lines_count
- - self._status_bar_lines_count
- - self._preview_lines_count
- - self._search_lines_count
- )
-
- def keep_visible(self, cursor_position: Optional[int], refresh_terminal_size: bool = True) -> None:
- # Treat `cursor_position=None` like `cursor_position=0`
- if cursor_position is None:
- cursor_position = 0
- if refresh_terminal_size:
- self.update_terminal_size()
- if self._viewport[0] <= cursor_position <= self._viewport[1]:
- # Cursor is already visible
- return
- if cursor_position < self._viewport[0]:
- scroll_num = cursor_position - self._viewport[0]
- else:
- scroll_num = cursor_position - self._viewport[1]
- self._viewport = (self._viewport[0] + scroll_num, self._viewport[1] + scroll_num)
-
- def update_terminal_size(self) -> None:
- num_lines = self._calculate_num_lines()
- if num_lines != self._num_lines:
- # First let the upper index grow or shrink
- upper_index = min(num_lines, self._num_menu_entries) - 1
- # Then, use as much space as possible for the `lower_index`
- lower_index = max(0, upper_index - num_lines)
- self._viewport = (lower_index, upper_index)
- self._num_lines = num_lines
-
- @property
- def lower_index(self) -> int:
- return self._viewport[0]
-
- @property
- def upper_index(self) -> int:
- return self._viewport[1]
-
- @property
- def viewport(self) -> Tuple[int, int]:
- return self._viewport
-
- @property
- def size(self) -> int:
- return self._viewport[1] - self._viewport[0] + 1
-
- @property
- def num_menu_entries(self) -> int:
- return self._num_menu_entries
-
- @property
- def title_lines_count(self) -> int:
- return self._title_lines_count
-
- @property
- def status_bar_lines_count(self) -> int:
- return self._status_bar_lines_count
-
- @status_bar_lines_count.setter
- def status_bar_lines_count(self, value: int) -> None:
- self._status_bar_lines_count = value
-
- @property
- def preview_lines_count(self) -> int:
- return self._preview_lines_count
-
- @preview_lines_count.setter
- def preview_lines_count(self, value: int) -> None:
- self._preview_lines_count = min(
- value if value >= 3 else 0,
- TerminalMenu._num_lines()
- - self._title_lines_count
- - self._status_bar_lines_count
- - MIN_VISIBLE_MENU_ENTRIES_COUNT,
- )
-
- @property
- def search_lines_count(self) -> int:
- return self._search_lines_count
-
- @search_lines_count.setter
- def search_lines_count(self, value: int) -> None:
- self._search_lines_count = value
-
- @property
- def must_scroll(self) -> bool:
- return self._num_menu_entries > self._num_lines
-
- _codename_to_capname = {
- "bg_black": "setab 0",
- "bg_blue": "setab 4",
- "bg_cyan": "setab 6",
- "bg_gray": "setab 7",
- "bg_green": "setab 2",
- "bg_purple": "setab 5",
- "bg_red": "setab 1",
- "bg_yellow": "setab 3",
- "bold": "bold",
- "clear": "clear",
- "colors": "colors",
- "cursor_down": "cud1",
- "cursor_invisible": "civis",
- "cursor_left": "cub1",
- "cursor_right": "cuf1",
- "cursor_up": "cuu1",
- "cursor_visible": "cnorm",
- "delete_line": "dl1",
- "down": "kcud1",
- "enter_application_mode": "smkx",
- "exit_application_mode": "rmkx",
- "fg_black": "setaf 0",
- "fg_blue": "setaf 4",
- "fg_cyan": "setaf 6",
- "fg_gray": "setaf 7",
- "fg_green": "setaf 2",
- "fg_purple": "setaf 5",
- "fg_red": "setaf 1",
- "fg_yellow": "setaf 3",
- "italics": "sitm",
- "reset_attributes": "sgr0",
- "standout": "smso",
- "underline": "smul",
- "up": "kcuu1",
- }
- _name_to_control_character = {
- "backspace": "", # Is assigned later in `self._init_backspace_control_character`
- "ctrl-j": "\012",
- "ctrl-k": "\013",
- "enter": "\015",
- "escape": "\033",
- "tab": "\t",
- }
- _codenames = tuple(_codename_to_capname.keys())
- _codename_to_terminal_code = None # type: Optional[Dict[str, str]]
- _terminal_code_to_codename = None # type: Optional[Dict[str, str]]
-
- def __init__(
- self,
- menu_entries: Iterable[str],
- *,
- accept_keys: Iterable[str] = DEFAULT_ACCEPT_KEYS,
- clear_menu_on_exit: bool = DEFAULT_CLEAR_MENU_ON_EXIT,
- clear_screen: bool = DEFAULT_CLEAR_SCREEN,
- cursor_index: Optional[int] = None,
- cycle_cursor: bool = DEFAULT_CYCLE_CURSOR,
- exit_on_shortcut: bool = DEFAULT_EXIT_ON_SHORTCUT,
- menu_cursor: Optional[str] = DEFAULT_MENU_CURSOR,
- menu_cursor_style: Optional[Iterable[str]] = DEFAULT_MENU_CURSOR_STYLE,
- menu_highlight_style: Optional[Iterable[str]] = DEFAULT_MENU_HIGHLIGHT_STYLE,
- multi_select: bool = DEFAULT_MULTI_SELECT,
- multi_select_cursor: str = DEFAULT_MULTI_SELECT_CURSOR,
- multi_select_cursor_brackets_style: Optional[Iterable[str]] = DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE,
- multi_select_cursor_style: Optional[Iterable[str]] = DEFAULT_MULTI_SELECT_CURSOR_STYLE,
- multi_select_empty_ok: bool = False,
- multi_select_keys: Optional[Iterable[str]] = DEFAULT_MULTI_SELECT_KEYS,
- multi_select_select_on_accept: bool = DEFAULT_MULTI_SELECT_SELECT_ON_ACCEPT,
- preselected_entries: Optional[Iterable[Union[str, int]]] = None,
- preview_border: bool = DEFAULT_PREVIEW_BORDER,
- preview_command: Optional[Union[str, Callable[[str], str]]] = None,
- preview_size: float = DEFAULT_PREVIEW_SIZE,
- preview_title: str = DEFAULT_PREVIEW_TITLE,
- quit_keys: Iterable[str] = DEFAULT_QUIT_KEYS,
- raise_error_on_interrupt: bool = False,
- search_case_sensitive: bool = DEFAULT_SEARCH_CASE_SENSITIVE,
- search_highlight_style: Optional[Iterable[str]] = DEFAULT_SEARCH_HIGHLIGHT_STYLE,
- search_key: Optional[str] = DEFAULT_SEARCH_KEY,
- shortcut_brackets_highlight_style: Optional[Iterable[str]] = DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE,
- shortcut_key_highlight_style: Optional[Iterable[str]] = DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE,
- show_multi_select_hint: bool = DEFAULT_SHOW_MULTI_SELECT_HINT,
- show_multi_select_hint_text: Optional[str] = None,
- show_search_hint: bool = DEFAULT_SHOW_SEARCH_HINT,
- show_search_hint_text: Optional[str] = None,
- show_shortcut_hints: bool = DEFAULT_SHOW_SHORTCUT_HINTS,
- show_shortcut_hints_in_status_bar: bool = DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR,
- skip_empty_entries: bool = False,
- status_bar: Optional[Union[str, Iterable[str], Callable[[str], str]]] = None,
- status_bar_below_preview: bool = DEFAULT_STATUS_BAR_BELOW_PREVIEW,
- status_bar_style: Optional[Iterable[str]] = DEFAULT_STATUS_BAR_STYLE,
- title: Optional[Union[str, Iterable[str]]] = None
- ):
- def extract_shortcuts_menu_entries_and_preview_arguments(
- entries: Iterable[str],
- ) -> Tuple[List[str], List[Optional[str]], List[Optional[str]], List[int]]:
- separator_pattern = re.compile(r"([^\\])\|")
- escaped_separator_pattern = re.compile(r"\\\|")
- menu_entry_pattern = re.compile(r"^(?:\[(\S)\]\s*)?([^\x1F]+)(?:\x1F([^\x1F]*))?")
- shortcut_keys = [] # type: List[Optional[str]]
- menu_entries = [] # type: List[str]
- preview_arguments = [] # type: List[Optional[str]]
- skip_indices = [] # type: List[int]
-
- for idx, entry in enumerate(entries):
- if entry is None or (entry == "" and skip_empty_entries):
- shortcut_keys.append(None)
- menu_entries.append("")
- preview_arguments.append(None)
- skip_indices.append(idx)
- else:
- unit_separated_entry = escaped_separator_pattern.sub("|", separator_pattern.sub("\\1\x1F", entry))
- match_obj = menu_entry_pattern.match(unit_separated_entry)
- # this is none in case the entry was an emtpy string which
- # will be interpreted as a separator
- assert match_obj is not None
- shortcut_key = match_obj.group(1)
- display_text = match_obj.group(2)
- preview_argument = match_obj.group(3)
- shortcut_keys.append(shortcut_key)
- menu_entries.append(display_text)
- preview_arguments.append(preview_argument)
-
- return menu_entries, shortcut_keys, preview_arguments, skip_indices
-
- def convert_preselected_entries_to_indices(
- preselected_indices_or_entries: Iterable[Union[str, int]]
- ) -> Set[int]:
- menu_entry_to_indices = {} # type: Dict[str, Set[int]]
- for menu_index, menu_entry in enumerate(self._menu_entries):
- menu_entry_to_indices.setdefault(menu_entry, set())
- menu_entry_to_indices[menu_entry].add(menu_index)
- preselected_indices = set()
- for item in preselected_indices_or_entries:
- if isinstance(item, int):
- if 0 <= item < len(self._menu_entries):
- preselected_indices.add(item)
- else:
- raise IndexError(
- "Error: {} is outside the allowable range of 0..{}.".format(
- item, len(self._menu_entries) - 1
- )
- )
- elif isinstance(item, str):
- try:
- preselected_indices.update(menu_entry_to_indices[item])
- except KeyError as e:
- raise UnknownMenuEntryError('Pre-selection "{}" is not a valid menu entry.'.format(item)) from e
- else:
- raise ValueError('"preselected_entries" must either contain integers or strings.')
- return preselected_indices
-
- def setup_title_or_status_bar_lines(
- title_or_status_bar: Optional[Union[str, Iterable[str]]],
- show_shortcut_hints: bool,
- menu_entries: Iterable[str],
- shortcut_keys: Iterable[Optional[str]],
- shortcut_hints_in_parentheses: bool,
- ) -> Tuple[str, ...]:
- if title_or_status_bar is None:
- lines = [] # type: List[str]
- elif isinstance(title_or_status_bar, str):
- lines = title_or_status_bar.split("\n")
- else:
- lines = list(title_or_status_bar)
- if show_shortcut_hints:
- shortcut_hints_line = self._get_shortcut_hints_line(
- menu_entries, shortcut_keys, shortcut_hints_in_parentheses
- )
- if shortcut_hints_line is not None:
- lines.append(shortcut_hints_line)
- return tuple(lines)
-
- (
- self._menu_entries,
- self._shortcut_keys,
- self._preview_arguments,
- self._skip_indices,
- ) = extract_shortcuts_menu_entries_and_preview_arguments(menu_entries)
- self._shortcuts_defined = any(key is not None for key in self._shortcut_keys)
- self._accept_keys = tuple(accept_keys)
- self._clear_menu_on_exit = clear_menu_on_exit
- self._clear_screen = clear_screen
- self._cycle_cursor = cycle_cursor
- self._multi_select_empty_ok = multi_select_empty_ok
- self._exit_on_shortcut = exit_on_shortcut
- self._menu_cursor = menu_cursor if menu_cursor is not None else ""
- self._menu_cursor_style = tuple(menu_cursor_style) if menu_cursor_style is not None else ()
- self._menu_highlight_style = tuple(menu_highlight_style) if menu_highlight_style is not None else ()
- self._multi_select = multi_select
- self._multi_select_cursor = multi_select_cursor
- self._multi_select_cursor_brackets_style = (
- tuple(multi_select_cursor_brackets_style) if multi_select_cursor_brackets_style is not None else ()
- )
- self._multi_select_cursor_style = (
- tuple(multi_select_cursor_style) if multi_select_cursor_style is not None else ()
- )
- self._multi_select_keys = tuple(multi_select_keys) if multi_select_keys is not None else ()
- self._multi_select_select_on_accept = multi_select_select_on_accept
- if preselected_entries and not self._multi_select:
- raise InvalidParameterCombinationError(
- "Multi-select mode must be enabled when preselected entries are given."
- )
- self._preselected_indices = (
- convert_preselected_entries_to_indices(preselected_entries) if preselected_entries is not None else None
- )
- self._preview_border = preview_border
- self._preview_command = preview_command
- self._preview_size = preview_size
- self._preview_title = preview_title
- self._quit_keys = tuple(quit_keys)
- self._raise_error_on_interrupt = raise_error_on_interrupt
- self._search_case_sensitive = search_case_sensitive
- self._search_highlight_style = tuple(search_highlight_style) if search_highlight_style is not None else ()
- self._search_key = search_key
- self._shortcut_brackets_highlight_style = (
- tuple(shortcut_brackets_highlight_style) if shortcut_brackets_highlight_style is not None else ()
- )
- self._shortcut_key_highlight_style = (
- tuple(shortcut_key_highlight_style) if shortcut_key_highlight_style is not None else ()
- )
- self._show_search_hint = show_search_hint
- self._show_search_hint_text = show_search_hint_text
- self._show_shortcut_hints = show_shortcut_hints
- self._show_shortcut_hints_in_status_bar = show_shortcut_hints_in_status_bar
- self._status_bar_func = None # type: Optional[Callable[[str], str]]
- self._status_bar_lines = None # type: Optional[Tuple[str, ...]]
- if callable(status_bar):
- self._status_bar_func = status_bar
- else:
- self._status_bar_lines = setup_title_or_status_bar_lines(
- status_bar,
- show_shortcut_hints and show_shortcut_hints_in_status_bar,
- self._menu_entries,
- self._shortcut_keys,
- False,
- )
- self._status_bar_below_preview = status_bar_below_preview
- self._status_bar_style = tuple(status_bar_style) if status_bar_style is not None else ()
- self._title_lines = setup_title_or_status_bar_lines(
- title,
- show_shortcut_hints and not show_shortcut_hints_in_status_bar,
- self._menu_entries,
- self._shortcut_keys,
- True,
- )
- self._show_multi_select_hint = show_multi_select_hint
- self._show_multi_select_hint_text = show_multi_select_hint_text
- self._chosen_accept_key = None # type: Optional[str]
- self._chosen_menu_index = None # type: Optional[int]
- self._chosen_menu_indices = None # type: Optional[Tuple[int, ...]]
- self._paint_before_next_read = False
- self._previous_displayed_menu_height = None # type: Optional[int]
- self._reading_next_key = False
- self._search = self.Search(
- self._menu_entries,
- case_senitive=self._search_case_sensitive,
- show_search_hint=self._show_search_hint,
- )
- self._selection = self.Selection(len(self._menu_entries), self._preselected_indices)
- self._viewport = self.Viewport(
- len(self._menu_entries),
- len(self._title_lines),
- len(self._status_bar_lines) if self._status_bar_lines is not None else 0,
- 0,
- 0,
- )
- self._view = self.View(
- self._menu_entries, self._search, self._selection, self._viewport, self._cycle_cursor, self._skip_indices
- )
- if cursor_index and 0 < cursor_index < len(self._menu_entries):
- self._view.active_menu_index = cursor_index
- self._search.change_callback = self._view.update_view
- self._old_term = None # type: Optional[List[Union[int, List[bytes]]]]
- self._new_term = None # type: Optional[List[Union[int, List[bytes]]]]
- self._tty_in = None # type: Optional[TextIO]
- self._tty_out = None # type: Optional[TextIO]
- self._user_locale = get_locale()
- self._check_for_valid_styles()
- # backspace can be queried from the terminal database but is unreliable, query the terminal directly instead
- self._init_backspace_control_character()
- self._add_missing_control_characters_for_keys(self._accept_keys)
- self._add_missing_control_characters_for_keys(self._quit_keys)
- self._init_terminal_codes()
-
- @staticmethod
- def _get_shortcut_hints_line(
- menu_entries: Iterable[str],
- shortcut_keys: Iterable[Optional[str]],
- shortcut_hints_in_parentheses: bool,
- ) -> Optional[str]:
- shortcut_hints_line = ", ".join(
- "[{}]: {}".format(shortcut_key, menu_entry)
- for shortcut_key, menu_entry in zip(shortcut_keys, menu_entries)
- if shortcut_key is not None
- )
- if shortcut_hints_line != "":
- if shortcut_hints_in_parentheses:
- return "(" + shortcut_hints_line + ")"
- else:
- return shortcut_hints_line
- return None
-
- @staticmethod
- def _get_keycode_for_key(key: str) -> str:
- if len(key) == 1:
- # One letter keys represent themselves
- return key
- alt_modified_regex = re.compile(r"[Aa]lt-(\S)")
- ctrl_modified_regex = re.compile(r"[Cc]trl-(\S)")
- match_obj = alt_modified_regex.match(key)
- if match_obj:
- return "\033" + match_obj.group(1)
- match_obj = ctrl_modified_regex.match(key)
- if match_obj:
- # Ctrl + key is interpreted by terminals as the ascii code of that key minus 64
- ctrl_code_ascii = ord(match_obj.group(1).upper()) - 64
- if ctrl_code_ascii < 0:
- # Interpret negative ascii codes as unsigned 7-Bit integers
- ctrl_code_ascii = ctrl_code_ascii & 0x80 - 1
- return chr(ctrl_code_ascii)
- raise ValueError('Cannot interpret the given key "{}".'.format(key))
-
- @classmethod
- def _init_backspace_control_character(self) -> None:
- try:
- with open("/dev/tty", "r") as tty:
- stty_output = subprocess.check_output(["stty", "-a"], universal_newlines=True, stdin=tty)
- name_to_keycode_regex = re.compile(r"^\s*(\S+)\s*=\s*\^(\S+)\s*$")
- for field in stty_output.split(";"):
- match_obj = name_to_keycode_regex.match(field)
- if not match_obj:
- continue
- name, ctrl_code = match_obj.group(1), match_obj.group(2)
- if name != "erase":
- continue
- self._name_to_control_character["backspace"] = self._get_keycode_for_key("ctrl-" + ctrl_code)
- return
- except subprocess.CalledProcessError:
- pass
- # Backspace control character could not be queried, assume `<Ctrl-?>` (is most often used)
- self._name_to_control_character["backspace"] = "\177"
-
- @classmethod
- def _add_missing_control_characters_for_keys(cls, keys: Iterable[str]) -> None:
- for key in keys:
- if key not in cls._name_to_control_character and key not in string.ascii_letters:
- cls._name_to_control_character[key] = cls._get_keycode_for_key(key)
-
- @classmethod
- def _init_terminal_codes(cls) -> None:
- if cls._codename_to_terminal_code is not None:
- return
- supported_colors = int(cls._query_terminfo_database("colors"))
- cls._codename_to_terminal_code = {
- codename: cls._query_terminfo_database(codename)
- if not (codename.startswith("bg_") or codename.startswith("fg_")) or supported_colors >= 8
- else ""
- for codename in cls._codenames
- }
- cls._codename_to_terminal_code.update(cls._name_to_control_character)
- cls._terminal_code_to_codename = {
- terminal_code: codename for codename, terminal_code in cls._codename_to_terminal_code.items()
- }
-
- @classmethod
- def _query_terminfo_database(cls, codename: str) -> str:
- if codename in cls._codename_to_capname:
- capname = cls._codename_to_capname[codename]
- else:
- capname = codename
- try:
- return subprocess.check_output(["tput"] + capname.split(), universal_newlines=True)
- except subprocess.CalledProcessError as e:
- # The return code 1 indicates a missing terminal capability
- if e.returncode == 1:
- return ""
- raise e
-
- @classmethod
- def _num_lines(self) -> int:
- return int(self._query_terminfo_database("lines"))
-
- @classmethod
- def _num_cols(self) -> int:
- return int(self._query_terminfo_database("cols"))
-
- def _check_for_valid_styles(self) -> None:
- invalid_styles = []
- for style_tuple in (
- self._menu_cursor_style,
- self._menu_highlight_style,
- self._search_highlight_style,
- self._shortcut_key_highlight_style,
- self._shortcut_brackets_highlight_style,
- self._status_bar_style,
- self._multi_select_cursor_brackets_style,
- self._multi_select_cursor_style,
- ):
- for style in style_tuple:
- if style not in self._codename_to_capname:
- invalid_styles.append(style)
- if invalid_styles:
- if len(invalid_styles) == 1:
- raise InvalidStyleError('The style "{}" does not exist.'.format(invalid_styles[0]))
- else:
- raise InvalidStyleError('The styles ("{}") do not exist.'.format('", "'.join(invalid_styles)))
-
- def _init_term(self) -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- self._tty_in = open("/dev/tty", "r", encoding=self._user_locale)
- self._tty_out = open("/dev/tty", "w", encoding=self._user_locale, errors="replace")
- self._old_term = termios.tcgetattr(self._tty_in.fileno())
- self._new_term = termios.tcgetattr(self._tty_in.fileno())
- # set the terminal to: unbuffered, no echo and no <CR> to <NL> translation (so <enter> sends <CR> instead of
- # <NL, this is necessary to distinguish between <enter> and <Ctrl-j> since <Ctrl-j> generates <NL>)
- self._new_term[3] = cast(int, self._new_term[3]) & ~termios.ICANON & ~termios.ECHO & ~termios.ICRNL
- self._new_term[0] = cast(int, self._new_term[0]) & ~termios.ICRNL
- termios.tcsetattr(
- self._tty_in.fileno(), termios.TCSAFLUSH, cast(List[Union[int, List[Union[bytes, int]]]], self._new_term)
- )
- # Enter terminal application mode to get expected escape codes for arrow keys
- self._tty_out.write(self._codename_to_terminal_code["enter_application_mode"])
- self._tty_out.write(self._codename_to_terminal_code["cursor_invisible"])
- if self._clear_screen:
- self._tty_out.write(self._codename_to_terminal_code["clear"])
-
- def _reset_term(self) -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_in is not None
- assert self._tty_out is not None
- assert self._old_term is not None
- termios.tcsetattr(
- self._tty_out.fileno(), termios.TCSAFLUSH, cast(List[Union[int, List[Union[bytes, int]]]], self._old_term)
- )
- self._tty_out.write(self._codename_to_terminal_code["cursor_visible"])
- self._tty_out.write(self._codename_to_terminal_code["exit_application_mode"])
- if self._clear_screen:
- self._tty_out.write(self._codename_to_terminal_code["clear"])
- self._tty_in.close()
- self._tty_out.close()
-
- def _paint_menu(self) -> None:
- def get_status_bar_lines() -> Tuple[str, ...]:
- def get_multi_select_hint() -> str:
- def get_string_from_keys(keys: Sequence[str]) -> str:
- string_to_key = {
- " ": "space",
- }
- keys_string = ", ".join(
- "<" + string_to_key.get(accept_key, accept_key) + ">" for accept_key in keys
- )
- return keys_string
-
- accept_keys_string = get_string_from_keys(self._accept_keys)
- multi_select_keys_string = get_string_from_keys(self._multi_select_keys)
- if self._show_multi_select_hint_text is not None:
- return self._show_multi_select_hint_text.format(
- multi_select_keys=multi_select_keys_string, accept_keys=accept_keys_string
- )
- else:
- return "Press {} for multi-selection and {} to {}accept".format(
- multi_select_keys_string,
- accept_keys_string,
- "select and " if self._multi_select_select_on_accept else "",
- )
-
- if self._status_bar_func is not None and self._view.active_menu_index is not None:
- status_bar_lines = tuple(
- self._status_bar_func(self._menu_entries[self._view.active_menu_index]).strip().split("\n")
- )
- if self._show_shortcut_hints and self._show_shortcut_hints_in_status_bar:
- shortcut_hints_line = self._get_shortcut_hints_line(self._menu_entries, self._shortcut_keys, False)
- if shortcut_hints_line is not None:
- status_bar_lines += (shortcut_hints_line,)
- elif self._status_bar_lines is not None:
- status_bar_lines = self._status_bar_lines
- else:
- status_bar_lines = tuple()
- if self._multi_select and self._show_multi_select_hint:
- status_bar_lines += (get_multi_select_hint(),)
- return status_bar_lines
-
- def apply_style(
- style_iterable: Optional[Iterable[str]] = None, reset: bool = True, file: Optional[TextIO] = None
- ) -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- if file is None:
- file = self._tty_out
- if reset or style_iterable is None:
- file.write(self._codename_to_terminal_code["reset_attributes"])
- if style_iterable is not None:
- for style in style_iterable:
- file.write(self._codename_to_terminal_code[style])
-
- def print_menu_entries() -> int:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- all_cursors_width = wcswidth(self._menu_cursor) + (
- wcswidth(self._multi_select_cursor) if self._multi_select else 0
- )
- current_menu_block_displayed_height = 0 # sum all written lines
- num_cols = self._num_cols()
- if self._title_lines:
- self._tty_out.write(
- len(self._title_lines) * self._codename_to_terminal_code["cursor_up"]
- + "\r"
- + "\n".join(
- (title_line[:num_cols] + (num_cols - wcswidth(title_line)) * " ")
- for title_line in self._title_lines
- )
- + "\n"
- )
- shortcut_string_len = 4 if self._shortcuts_defined else 0
- displayed_index = -1
- for displayed_index, menu_index, menu_entry in self._view:
- current_shortcut_key = self._shortcut_keys[menu_index]
- self._tty_out.write(all_cursors_width * self._codename_to_terminal_code["cursor_right"])
- if self._shortcuts_defined:
- if current_shortcut_key is not None:
- apply_style(self._shortcut_brackets_highlight_style)
- self._tty_out.write("[")
- apply_style(self._shortcut_key_highlight_style)
- self._tty_out.write(current_shortcut_key)
- apply_style(self._shortcut_brackets_highlight_style)
- self._tty_out.write("]")
- apply_style()
- else:
- self._tty_out.write(3 * " ")
- self._tty_out.write(" ")
- if menu_index == self._view.active_menu_index:
- apply_style(self._menu_highlight_style)
- if self._search and self._search.search_text != "":
- match_obj = self._search.matches[displayed_index][1]
- self._tty_out.write(
- menu_entry[: min(match_obj.start(), num_cols - all_cursors_width - shortcut_string_len)]
- )
- apply_style(self._search_highlight_style)
- self._tty_out.write(
- menu_entry[
- match_obj.start() : min(match_obj.end(), num_cols - all_cursors_width - shortcut_string_len)
- ]
- )
- apply_style()
- if menu_index == self._view.active_menu_index:
- apply_style(self._menu_highlight_style)
- self._tty_out.write(
- menu_entry[match_obj.end() : num_cols - all_cursors_width - shortcut_string_len]
- )
- else:
- self._tty_out.write(menu_entry[: num_cols - all_cursors_width - shortcut_string_len])
- if menu_index == self._view.active_menu_index:
- apply_style()
- self._tty_out.write((num_cols - wcswidth(menu_entry) - all_cursors_width - shortcut_string_len) * " ")
- if displayed_index < self._viewport.upper_index:
- self._tty_out.write("\n")
- empty_menu_lines = self._viewport.upper_index - displayed_index
- self._tty_out.write(
- max(0, empty_menu_lines - 1) * (num_cols * " " + "\n") + min(1, empty_menu_lines) * (num_cols * " ")
- )
- self._tty_out.write("\r" + (self._viewport.size - 1) * self._codename_to_terminal_code["cursor_up"])
- current_menu_block_displayed_height += self._viewport.size - 1 # sum all written lines
- return current_menu_block_displayed_height
-
- def print_search_line(current_menu_height: int) -> int:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- current_menu_block_displayed_height = 0
- num_cols = self._num_cols()
- if self._search or self._show_search_hint:
- self._tty_out.write((current_menu_height + 1) * self._codename_to_terminal_code["cursor_down"])
- if self._search:
- assert self._search.search_text is not None
- self._tty_out.write(
- (
- (self._search_key if self._search_key is not None else DEFAULT_SEARCH_KEY)
- + self._search.search_text
- )[:num_cols]
- )
- self._tty_out.write((num_cols - len(self._search) - 1) * " ")
- elif self._show_search_hint:
- if self._show_search_hint_text is not None:
- search_hint = self._show_search_hint_text.format(key=self._search_key)[:num_cols]
- elif self._search_key is not None:
- search_hint = '(Press "{key}" to search)'.format(key=self._search_key)[:num_cols]
- else:
- search_hint = "(Press any letter key to search)"[:num_cols]
- self._tty_out.write(search_hint)
- self._tty_out.write((num_cols - wcswidth(search_hint)) * " ")
- if self._search or self._show_search_hint:
- self._tty_out.write("\r" + (current_menu_height + 1) * self._codename_to_terminal_code["cursor_up"])
- current_menu_block_displayed_height = 1
- return current_menu_block_displayed_height
-
- def print_status_bar(current_menu_height: int, status_bar_lines: Tuple[str, ...]) -> int:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- current_menu_block_displayed_height = 0 # sum all written lines
- num_cols = self._num_cols()
- if status_bar_lines:
- self._tty_out.write((current_menu_height + 1) * self._codename_to_terminal_code["cursor_down"])
- apply_style(self._status_bar_style)
- self._tty_out.write(
- "\r"
- + "\n".join(
- (status_bar_line[:num_cols] + (num_cols - wcswidth(status_bar_line)) * " ")
- for status_bar_line in status_bar_lines
- )
- + "\r"
- )
- apply_style()
- self._tty_out.write(
- (current_menu_height + len(status_bar_lines)) * self._codename_to_terminal_code["cursor_up"]
- )
- current_menu_block_displayed_height += len(status_bar_lines)
- return current_menu_block_displayed_height
-
- def print_preview(current_menu_height: int, preview_max_num_lines: int) -> int:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- if self._preview_command is None or preview_max_num_lines < 3:
- return 0
-
- def get_preview_string() -> Optional[str]:
- assert self._preview_command is not None
- if self._view.active_menu_index is None:
- return None
- preview_argument = (
- self._preview_arguments[self._view.active_menu_index]
- if self._preview_arguments[self._view.active_menu_index] is not None
- else self._menu_entries[self._view.active_menu_index]
- )
- if preview_argument == "":
- return None
- if isinstance(self._preview_command, str):
- try:
- preview_process = subprocess.Popen(
- [cmd_part.format(preview_argument) for cmd_part in shlex.split(self._preview_command)],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
- assert preview_process.stdout is not None
- preview_string = (
- io.TextIOWrapper(preview_process.stdout, encoding=self._user_locale, errors="replace")
- .read()
- .strip()
- )
- except subprocess.CalledProcessError as e:
- raise PreviewCommandFailedError(
- e.stderr.decode(encoding=self._user_locale, errors="replace").strip()
- ) from e
- else:
- preview_string = self._preview_command(preview_argument) if preview_argument is not None else ""
- return preview_string
-
- @static_variables(
- # Regex taken from https://stackoverflow.com/a/14693789/5958465
- ansi_escape_regex=re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"),
- # Modified version of https://stackoverflow.com/a/2188410/5958465
- ansi_sgr_regex=re.compile(r"\x1B\[[;\d]*m"),
- )
- def strip_ansi_codes_except_styling(string: str) -> str:
- stripped_string = strip_ansi_codes_except_styling.ansi_escape_regex.sub( # type: ignore
- lambda match_obj: match_obj.group(0)
- if strip_ansi_codes_except_styling.ansi_sgr_regex.match(match_obj.group(0)) # type: ignore
- else "",
- string,
- )
- return cast(str, stripped_string)
-
- @static_variables(
- regular_text_regex=re.compile(r"([^\x1B]+)(.*)"),
- ansi_escape_regex=re.compile(r"(\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))(.*)"),
- )
- def limit_string_with_escape_codes(string: str, max_len: int) -> Tuple[str, int]:
- if max_len <= 0:
- return "", 0
- string_parts = []
- string_len = 0
- while string:
- regular_text_match = limit_string_with_escape_codes.regular_text_regex.match(string) # type: ignore
- if regular_text_match is not None:
- regular_text = regular_text_match.group(1)
- regular_text_len = wcswidth(regular_text)
- if string_len + regular_text_len > max_len:
- string_parts.append(regular_text[: max_len - string_len])
- string_len = max_len
- break
- string_parts.append(regular_text)
- string_len += regular_text_len
- string = regular_text_match.group(2)
- else:
- ansi_escape_match = limit_string_with_escape_codes.ansi_escape_regex.match( # type: ignore
- string
- )
- if ansi_escape_match is not None:
- # Adopt the ansi escape code but do not count its length
- ansi_escape_code_text = ansi_escape_match.group(1)
- string_parts.append(ansi_escape_code_text)
- string = ansi_escape_match.group(2)
- else:
- # It looks like an escape code (starts with escape), but it is something else
- # -> skip the escape character and continue the loop
- string_parts.append("\x1B")
- string = string[1:]
- return "".join(string_parts), string_len
-
- num_cols = self._num_cols()
- try:
- preview_string = get_preview_string()
- if preview_string is not None:
- preview_string = strip_ansi_codes_except_styling(preview_string)
- except PreviewCommandFailedError as e:
- preview_string = "The preview command failed with error message:\n\n" + str(e)
- self._tty_out.write(current_menu_height * self._codename_to_terminal_code["cursor_down"])
- if preview_string is not None:
- self._tty_out.write(self._codename_to_terminal_code["cursor_down"] + "\r")
- if self._preview_border:
- self._tty_out.write(
- (
- BoxDrawingCharacters.upper_left
- + (2 * BoxDrawingCharacters.horizontal + " " + self._preview_title)[: num_cols - 3]
- + " "
- + (num_cols - len(self._preview_title) - 6) * BoxDrawingCharacters.horizontal
- + BoxDrawingCharacters.upper_right
- )[:num_cols]
- + "\n"
- )
- # `finditer` can be used as a generator version of `str.join`
- for i, line in enumerate(
- match.group(0) for match in re.finditer(r"^.*$", preview_string, re.MULTILINE)
- ):
- if i >= preview_max_num_lines - (2 if self._preview_border else 0):
- preview_num_lines = preview_max_num_lines
- break
- limited_line, limited_line_len = limit_string_with_escape_codes(
- line, num_cols - (3 if self._preview_border else 0)
- )
- self._tty_out.write(
- (
- ((BoxDrawingCharacters.vertical + " ") if self._preview_border else "")
- + limited_line
- + self._codename_to_terminal_code["reset_attributes"]
- + max(num_cols - limited_line_len - (3 if self._preview_border else 0), 0) * " "
- + (BoxDrawingCharacters.vertical if self._preview_border else "")
- )
- )
- else:
- preview_num_lines = i + (3 if self._preview_border else 1)
- if self._preview_border:
- self._tty_out.write(
- "\n"
- + (
- BoxDrawingCharacters.lower_left
- + (num_cols - 2) * BoxDrawingCharacters.horizontal
- + BoxDrawingCharacters.lower_right
- )[:num_cols]
- )
- self._tty_out.write("\r")
- else:
- preview_num_lines = 0
- self._tty_out.write(
- (current_menu_height + preview_num_lines) * self._codename_to_terminal_code["cursor_up"]
- )
- return preview_num_lines
-
- def delete_old_menu_lines(displayed_menu_height: int) -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- if (
- self._previous_displayed_menu_height is not None
- and self._previous_displayed_menu_height > displayed_menu_height
- ):
- self._tty_out.write((displayed_menu_height + 1) * self._codename_to_terminal_code["cursor_down"])
- self._tty_out.write(
- (self._previous_displayed_menu_height - displayed_menu_height)
- * self._codename_to_terminal_code["delete_line"]
- )
- self._tty_out.write((displayed_menu_height + 1) * self._codename_to_terminal_code["cursor_up"])
-
- def position_cursor() -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- if self._view.active_displayed_index is None:
- return
-
- cursor_width = wcswidth(self._menu_cursor)
- for displayed_index in range(self._viewport.lower_index, self._viewport.upper_index + 1):
- if displayed_index == self._view.active_displayed_index:
- apply_style(self._menu_cursor_style)
- self._tty_out.write(self._menu_cursor)
- apply_style()
- else:
- self._tty_out.write(cursor_width * " ")
- self._tty_out.write("\r")
- if displayed_index < self._viewport.upper_index:
- self._tty_out.write(self._codename_to_terminal_code["cursor_down"])
- self._tty_out.write((self._viewport.size - 1) * self._codename_to_terminal_code["cursor_up"])
-
- def print_multi_select_column() -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- if not self._multi_select:
- return
-
- def prepare_multi_select_cursors() -> Tuple[str, str]:
- bracket_characters = "([{<)]}>"
- bracket_style_escape_codes_io = io.StringIO()
- multi_select_cursor_style_escape_codes_io = io.StringIO()
- reset_codes_io = io.StringIO()
- apply_style(self._multi_select_cursor_brackets_style, file=bracket_style_escape_codes_io)
- apply_style(self._multi_select_cursor_style, file=multi_select_cursor_style_escape_codes_io)
- apply_style(file=reset_codes_io)
- bracket_style_escape_codes = bracket_style_escape_codes_io.getvalue()
- multi_select_cursor_style_escape_codes = multi_select_cursor_style_escape_codes_io.getvalue()
- reset_codes = reset_codes_io.getvalue()
-
- cursor_with_brackets_only = re.sub(
- r"[^{}]".format(re.escape(bracket_characters)), " ", self._multi_select_cursor
- )
- cursor_with_brackets_only_styled = re.sub(
- r"[{}]+".format(re.escape(bracket_characters)),
- lambda match_obj: bracket_style_escape_codes + match_obj.group(0) + reset_codes,
- cursor_with_brackets_only,
- )
- cursor_styled = re.sub(
- r"[{brackets}]+|[^{brackets}\s]+".format(brackets=re.escape(bracket_characters)),
- lambda match_obj: (
- bracket_style_escape_codes
- if match_obj.group(0)[0] in bracket_characters
- else multi_select_cursor_style_escape_codes
- )
- + match_obj.group(0)
- + reset_codes,
- self._multi_select_cursor,
- )
- return cursor_styled, cursor_with_brackets_only_styled
-
- if not self._view:
- return
- checked_multi_select_cursor, unchecked_multi_select_cursor = prepare_multi_select_cursors()
- cursor_width = wcswidth(self._menu_cursor)
- displayed_selected_indices = self._view.displayed_selected_indices
- displayed_index = 0
- for displayed_index, _, _ in self._view:
- self._tty_out.write("\r" + cursor_width * self._codename_to_terminal_code["cursor_right"])
- if displayed_index in self._skip_indices:
- self._tty_out.write("")
- elif displayed_index in displayed_selected_indices:
- self._tty_out.write(checked_multi_select_cursor)
- else:
- self._tty_out.write(unchecked_multi_select_cursor)
- if displayed_index < self._viewport.upper_index:
- self._tty_out.write(self._codename_to_terminal_code["cursor_down"])
- self._tty_out.write("\r")
- self._tty_out.write(
- (displayed_index + (1 if displayed_index < self._viewport.upper_index else 0))
- * self._codename_to_terminal_code["cursor_up"]
- )
-
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._tty_out is not None
- displayed_menu_height = 0 # sum all written lines
- status_bar_lines = get_status_bar_lines()
- self._viewport.status_bar_lines_count = len(status_bar_lines)
- if self._preview_command is not None:
- self._viewport.preview_lines_count = int(self._preview_size * self._num_lines())
- preview_max_num_lines = self._viewport.preview_lines_count
- self._viewport.keep_visible(self._view.active_displayed_index)
- displayed_menu_height += print_menu_entries()
- displayed_menu_height += print_search_line(displayed_menu_height)
- if not self._status_bar_below_preview:
- displayed_menu_height += print_status_bar(displayed_menu_height, status_bar_lines)
- if self._preview_command is not None:
- displayed_menu_height += print_preview(displayed_menu_height, preview_max_num_lines)
- if self._status_bar_below_preview:
- displayed_menu_height += print_status_bar(displayed_menu_height, status_bar_lines)
- delete_old_menu_lines(displayed_menu_height)
- position_cursor()
- if self._multi_select:
- print_multi_select_column()
- self._previous_displayed_menu_height = displayed_menu_height
- self._tty_out.flush()
-
- def _clear_menu(self) -> None:
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- assert self._previous_displayed_menu_height is not None
- assert self._tty_out is not None
- if self._clear_menu_on_exit:
- if self._title_lines:
- self._tty_out.write(len(self._title_lines) * self._codename_to_terminal_code["cursor_up"])
- self._tty_out.write(len(self._title_lines) * self._codename_to_terminal_code["delete_line"])
- self._tty_out.write(
- (self._previous_displayed_menu_height + 1) * self._codename_to_terminal_code["delete_line"]
- )
- else:
- self._tty_out.write(
- (self._previous_displayed_menu_height + 1) * self._codename_to_terminal_code["cursor_down"]
- )
- self._tty_out.flush()
-
- def _read_next_key(self, ignore_case: bool = True) -> str:
- # pylint: disable=unsubscriptable-object,unsupported-membership-test
- assert self._terminal_code_to_codename is not None
- assert self._tty_in is not None
- # Needed for asynchronous handling of terminal resize events
- self._reading_next_key = True
- if self._paint_before_next_read:
- self._paint_menu()
- self._paint_before_next_read = False
- # blocks until any amount of bytes is available
- code = os.read(self._tty_in.fileno(), 80).decode("ascii", errors="ignore")
- self._reading_next_key = False
- if code in self._terminal_code_to_codename:
- return self._terminal_code_to_codename[code]
- elif ignore_case:
- return code.lower()
- else:
- return code
-
- def show(self) -> Optional[Union[int, Tuple[int, ...]]]:
- def init_signal_handling() -> None:
- # `SIGWINCH` is send on terminal resizes
- def handle_sigwinch(signum: signal.Signals, frame: FrameType) -> None:
- # pylint: disable=unused-argument
- if self._reading_next_key:
- self._paint_menu()
- else:
- self._paint_before_next_read = True
-
- signal.signal(signal.SIGWINCH, handle_sigwinch)
-
- def reset_signal_handling() -> None:
- signal.signal(signal.SIGWINCH, signal.SIG_DFL)
-
- def remove_letter_keys(menu_action_to_keys: Dict[str, Set[Optional[str]]]) -> None:
- letter_keys = frozenset(string.ascii_lowercase) | frozenset(" ")
- for keys in menu_action_to_keys.values():
- keys -= letter_keys
-
- # pylint: disable=unsubscriptable-object
- assert self._codename_to_terminal_code is not None
- self._init_term()
- if self._preselected_indices is None:
- self._selection.clear()
- self._chosen_accept_key = None
- self._chosen_menu_indices = None
- self._chosen_menu_index = None
- assert self._tty_out is not None
- if self._title_lines:
- # `print_menu` expects the cursor on the first menu item -> reserve one line for the title
- self._tty_out.write(len(self._title_lines) * self._codename_to_terminal_code["cursor_down"])
- menu_was_interrupted = False
- try:
- init_signal_handling()
- menu_action_to_keys = {
- "menu_up": set(("up", "ctrl-k", "k")),
- "menu_down": set(("down", "ctrl-j", "j")),
- "accept": set(self._accept_keys),
- "multi_select": set(self._multi_select_keys),
- "quit": set(self._quit_keys),
- "search_start": set((self._search_key,)),
- "backspace": set(("backspace",)),
- } # type: Dict[str, Set[Optional[str]]]
- while True:
- self._paint_menu()
- current_menu_action_to_keys = copy.deepcopy(menu_action_to_keys)
- next_key = self._read_next_key(ignore_case=False)
- if self._search or self._search_key is None:
- remove_letter_keys(current_menu_action_to_keys)
- else:
- next_key = next_key.lower()
- if self._search_key is not None and not self._search and next_key in self._shortcut_keys:
- shortcut_menu_index = self._shortcut_keys.index(next_key)
- if self._exit_on_shortcut:
- self._selection.add(shortcut_menu_index)
- break
- else:
- if self._multi_select:
- self._selection.toggle(shortcut_menu_index)
- else:
- self._view.active_menu_index = shortcut_menu_index
- elif next_key in current_menu_action_to_keys["menu_up"]:
- self._view.decrement_active_index()
- elif next_key in current_menu_action_to_keys["menu_down"]:
- self._view.increment_active_index()
- elif self._multi_select and next_key in current_menu_action_to_keys["multi_select"]:
- if self._view.active_menu_index is not None:
- self._selection.toggle(self._view.active_menu_index)
- elif next_key in current_menu_action_to_keys["accept"]:
- if self._view.active_menu_index is not None:
- if self._multi_select_select_on_accept or (
- not self._selection and self._multi_select_empty_ok is False
- ):
- self._selection.add(self._view.active_menu_index)
- self._chosen_accept_key = next_key
- break
- elif next_key in current_menu_action_to_keys["quit"]:
- if not self._search:
- menu_was_interrupted = True
- break
- else:
- self._search.search_text = None
- elif not self._search:
- if next_key in current_menu_action_to_keys["search_start"] or (
- self._search_key is None and next_key == DEFAULT_SEARCH_KEY
- ):
- self._search.search_text = ""
- elif self._search_key is None:
- self._search.search_text = next_key
- else:
- assert self._search.search_text is not None
- if next_key in ("backspace",):
- if self._search.search_text != "":
- self._search.search_text = self._search.search_text[:-1]
- else:
- self._search.search_text = None
- elif wcswidth(next_key) >= 0 and not (
- next_key in current_menu_action_to_keys["search_start"] and self._search.search_text == ""
- ):
- # Only append `next_key` if it is a printable character and the first character is not the
- # `search_start` key
- self._search.search_text += next_key
- except KeyboardInterrupt as e:
- if self._raise_error_on_interrupt:
- raise e
- menu_was_interrupted = True
- finally:
- reset_signal_handling()
- self._clear_menu()
- self._reset_term()
- if not menu_was_interrupted:
- chosen_menu_indices = self._selection.selected_menu_indices
- if chosen_menu_indices:
- if self._multi_select:
- self._chosen_menu_indices = chosen_menu_indices
- else:
- self._chosen_menu_index = chosen_menu_indices[0]
- return self._chosen_menu_indices if self._multi_select else self._chosen_menu_index
-
- @property
- def chosen_accept_key(self) -> Optional[str]:
- return self._chosen_accept_key
-
- @property
- def chosen_menu_entry(self) -> Optional[str]:
- return self._menu_entries[self._chosen_menu_index] if self._chosen_menu_index is not None else None
-
- @property
- def chosen_menu_entries(self) -> Optional[Tuple[str, ...]]:
- return (
- tuple(self._menu_entries[menu_index] for menu_index in self._chosen_menu_indices)
- if self._chosen_menu_indices is not None
- else None
- )
-
- @property
- def chosen_menu_index(self) -> Optional[int]:
- return self._chosen_menu_index
-
- @property
- def chosen_menu_indices(self) -> Optional[Tuple[int, ...]]:
- return self._chosen_menu_indices
-
-
-class AttributeDict(dict): # type: ignore
- def __getattr__(self, attr: str) -> Any:
- return self[attr]
-
- def __setattr__(self, attr: str, value: Any) -> None:
- self[attr] = value
-
-
-def get_argumentparser() -> argparse.ArgumentParser:
- parser = argparse.ArgumentParser(
- formatter_class=argparse.RawDescriptionHelpFormatter,
- description="""
-%(prog)s creates simple interactive menus in the terminal and returns the selected entry as exit code.
-""",
- )
- parser.add_argument(
- "-s", "--case-sensitive", action="store_true", dest="case_sensitive", help="searches are case sensitive"
- )
- parser.add_argument(
- "-X",
- "--no-clear-menu-on-exit",
- action="store_false",
- dest="clear_menu_on_exit",
- help="do not clear the menu on exit",
- )
- parser.add_argument(
- "-l",
- "--clear-screen",
- action="store_true",
- dest="clear_screen",
- help="clear the screen before the menu is shown",
- )
- parser.add_argument(
- "--cursor",
- action="store",
- dest="cursor",
- default=DEFAULT_MENU_CURSOR,
- help='menu cursor (default: "%(default)s")',
- )
- parser.add_argument(
- "-i",
- "--cursor-index",
- action="store",
- dest="cursor_index",
- type=int,
- default=0,
- help="initially selected item index",
- )
- parser.add_argument(
- "--cursor-style",
- action="store",
- dest="cursor_style",
- default=",".join(DEFAULT_MENU_CURSOR_STYLE),
- help='style for the menu cursor as comma separated list (default: "%(default)s")',
- )
- parser.add_argument("-C", "--no-cycle", action="store_false", dest="cycle", help="do not cycle the menu selection")
- parser.add_argument(
- "-E",
- "--no-exit-on-shortcut",
- action="store_false",
- dest="exit_on_shortcut",
- help="do not exit on shortcut keys",
- )
- parser.add_argument(
- "--highlight-style",
- action="store",
- dest="highlight_style",
- default=",".join(DEFAULT_MENU_HIGHLIGHT_STYLE),
- help='style for the selected menu entry as comma separated list (default: "%(default)s")',
- )
- parser.add_argument(
- "-m",
- "--multi-select",
- action="store_true",
- dest="multi_select",
- help="Allow the selection of multiple entries (implies `--stdout`)",
- )
- parser.add_argument(
- "--multi-select-cursor",
- action="store",
- dest="multi_select_cursor",
- default=DEFAULT_MULTI_SELECT_CURSOR,
- help='multi-select menu cursor (default: "%(default)s")',
- )
- parser.add_argument(
- "--multi-select-cursor-brackets-style",
- action="store",
- dest="multi_select_cursor_brackets_style",
- default=",".join(DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE),
- help='style for brackets of the multi-select menu cursor as comma separated list (default: "%(default)s")',
- )
- parser.add_argument(
- "--multi-select-cursor-style",
- action="store",
- dest="multi_select_cursor_style",
- default=",".join(DEFAULT_MULTI_SELECT_CURSOR_STYLE),
- help='style for the multi-select menu cursor as comma separated list (default: "%(default)s")',
- )
- parser.add_argument(
- "--multi-select-keys",
- action="store",
- dest="multi_select_keys",
- default=",".join(DEFAULT_MULTI_SELECT_KEYS),
- help=('key for toggling a selected item in a multi-selection (default: "%(default)s", '),
- )
- parser.add_argument(
- "--multi-select-no-select-on-accept",
- action="store_false",
- dest="multi_select_select_on_accept",
- help=(
- "do not select the currently highlighted menu item when the accept key is pressed "
- "(it is still selected if no other item was selected before)"
- ),
- )
- parser.add_argument(
- "--multi-select-empty-ok",
- action="store_true",
- dest="multi_select_empty_ok",
- help=("when used together with --multi-select-no-select-on-accept allows returning no selection at all"),
- )
- parser.add_argument(
- "-p",
- "--preview",
- action="store",
- dest="preview_command",
- help=(
- "Command to generate a preview for the selected menu entry. "
- '"{}" can be used as placeholder for the menu text. '
- 'If the menu entry has a data component (separated by "|"), this is used instead.'
- ),
- )
- parser.add_argument(
- "--no-preview-border",
- action="store_false",
- dest="preview_border",
- help="do not draw a border around the preview window",
- )
- parser.add_argument(
- "--preview-size",
- action="store",
- dest="preview_size",
- type=float,
- default=DEFAULT_PREVIEW_SIZE,
- help='maximum height of the preview window in fractions of the terminal height (default: "%(default)s")',
- )
- parser.add_argument(
- "--preview-title",
- action="store",
- dest="preview_title",
- default=DEFAULT_PREVIEW_TITLE,
- help='title of the preview window (default: "%(default)s")',
- )
- parser.add_argument(
- "--search-highlight-style",
- action="store",
- dest="search_highlight_style",
- default=",".join(DEFAULT_SEARCH_HIGHLIGHT_STYLE),
- help='style of matched search patterns (default: "%(default)s")',
- )
- parser.add_argument(
- "--search-key",
- action="store",
- dest="search_key",
- default=DEFAULT_SEARCH_KEY,
- help=(
- 'key to start a search (default: "%(default)s", '
- '"none" is treated a special value which activates the search on any letter key)'
- ),
- )
- parser.add_argument(
- "--shortcut-brackets-highlight-style",
- action="store",
- dest="shortcut_brackets_highlight_style",
- default=",".join(DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE),
- help='style of brackets enclosing shortcut keys (default: "%(default)s")',
- )
- parser.add_argument(
- "--shortcut-key-highlight-style",
- action="store",
- dest="shortcut_key_highlight_style",
- default=",".join(DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE),
- help='style of shortcut keys (default: "%(default)s")',
- )
- parser.add_argument(
- "--show-multi-select-hint",
- action="store_true",
- dest="show_multi_select_hint",
- help="show a multi-select hint in the status bar",
- )
- parser.add_argument(
- "--show-multi-select-hint-text",
- action="store",
- dest="show_multi_select_hint_text",
- help=(
- "Custom text which will be shown as multi-select hint. Use the placeholders {multi_select_keys} and "
- "{accept_keys} if appropriately."
- ),
- )
- parser.add_argument(
- "--show-search-hint",
- action="store_true",
- dest="show_search_hint",
- help="show a search hint in the search line",
- )
- parser.add_argument(
- "--show-search-hint-text",
- action="store",
- dest="show_search_hint_text",
- help=(
- "Custom text which will be shown as search hint. Use the placeholders {key} for the search key "
- "if appropriately."
- ),
- )
- parser.add_argument(
- "--show-shortcut-hints",
- action="store_true",
- dest="show_shortcut_hints",
- help="show shortcut hints in the status bar",
- )
- parser.add_argument(
- "--show-shortcut-hints-in-title",
- action="store_false",
- dest="show_shortcut_hints_in_status_bar",
- default=True,
- help="show shortcut hints in the menu title",
- )
- parser.add_argument(
- "--skip-empty-entries",
- action="store_true",
- dest="skip_empty_entries",
- help="Interpret an empty string in menu entries as an empty menu entry",
- )
- parser.add_argument(
- "-b",
- "--status-bar",
- action="store",
- dest="status_bar",
- help="status bar text",
- )
- parser.add_argument(
- "-d",
- "--status-bar-below-preview",
- action="store_true",
- dest="status_bar_below_preview",
- help="show the status bar below the preview window if any",
- )
- parser.add_argument(
- "--status-bar-style",
- action="store",
- dest="status_bar_style",
- default=",".join(DEFAULT_STATUS_BAR_STYLE),
- help='style of the status bar lines (default: "%(default)s")',
- )
- parser.add_argument(
- "--stdout",
- action="store_true",
- dest="stdout",
- help=(
- "Print the selected menu index or indices to stdout (in addition to the exit status). "
- 'Multiple indices are separated by ";".'
- ),
- )
- parser.add_argument("-t", "--title", action="store", dest="title", help="menu title")
- parser.add_argument(
- "-V", "--version", action="store_true", dest="print_version", help="print the version number and exit"
- )
- parser.add_argument("entries", action="store", nargs="*", help="the menu entries to show")
- group = parser.add_mutually_exclusive_group()
- group.add_argument(
- "-r",
- "--preselected_entries",
- action="store",
- dest="preselected_entries",
- help="Comma separated list of strings matching menu items to start pre-selected in a multi-select menu.",
- )
- group.add_argument(
- "-R",
- "--preselected_indices",
- action="store",
- dest="preselected_indices",
- help="Comma separated list of numeric indexes of menu items to start pre-selected in a multi-select menu.",
- )
- return parser
-
-
-def parse_arguments() -> AttributeDict:
- parser = get_argumentparser()
- args = AttributeDict({key: value for key, value in vars(parser.parse_args()).items()})
- if not args.print_version and not args.entries:
- raise NoMenuEntriesError("No menu entries given!")
- if args.skip_empty_entries:
- args.entries = [entry if entry != "None" else None for entry in args.entries]
- if args.cursor_style != "":
- args.cursor_style = tuple(args.cursor_style.split(","))
- else:
- args.cursor_style = None
- if args.highlight_style != "":
- args.highlight_style = tuple(args.highlight_style.split(","))
- else:
- args.highlight_style = None
- if args.search_highlight_style != "":
- args.search_highlight_style = tuple(args.search_highlight_style.split(","))
- else:
- args.search_highlight_style = None
- if args.shortcut_key_highlight_style != "":
- args.shortcut_key_highlight_style = tuple(args.shortcut_key_highlight_style.split(","))
- else:
- args.shortcut_key_highlight_style = None
- if args.shortcut_brackets_highlight_style != "":
- args.shortcut_brackets_highlight_style = tuple(args.shortcut_brackets_highlight_style.split(","))
- else:
- args.shortcut_brackets_highlight_style = None
- if args.status_bar_style != "":
- args.status_bar_style = tuple(args.status_bar_style.split(","))
- else:
- args.status_bar_style = None
- if args.multi_select_cursor_brackets_style != "":
- args.multi_select_cursor_brackets_style = tuple(args.multi_select_cursor_brackets_style.split(","))
- else:
- args.multi_select_cursor_brackets_style = None
- if args.multi_select_cursor_style != "":
- args.multi_select_cursor_style = tuple(args.multi_select_cursor_style.split(","))
- else:
- args.multi_select_cursor_style = None
- if args.multi_select_keys != "":
- args.multi_select_keys = tuple(args.multi_select_keys.split(","))
- else:
- args.multi_select_keys = None
- if args.search_key.lower() == "none":
- args.search_key = None
- if args.show_shortcut_hints_in_status_bar:
- args.show_shortcut_hints = True
- if args.multi_select:
- args.stdout = True
- if args.preselected_entries is not None:
- args.preselected = list(args.preselected_entries.split(","))
- elif args.preselected_indices is not None:
- args.preselected = list(map(int, args.preselected_indices.split(",")))
- else:
- args.preselected = None
- return args
-
-
-def main() -> None:
- try:
- args = parse_arguments()
- except SystemExit:
- sys.exit(0) # Error code 0 is the error case in this program
- except NoMenuEntriesError as e:
- print(str(e), file=sys.stderr)
- sys.exit(0)
- if args.print_version:
- print("{}, version {}".format(os.path.basename(sys.argv[0]), __version__))
- sys.exit(0)
- try:
- terminal_menu = TerminalMenu(
- menu_entries=args.entries,
- clear_menu_on_exit=args.clear_menu_on_exit,
- clear_screen=args.clear_screen,
- cursor_index=args.cursor_index,
- cycle_cursor=args.cycle,
- exit_on_shortcut=args.exit_on_shortcut,
- menu_cursor=args.cursor,
- menu_cursor_style=args.cursor_style,
- menu_highlight_style=args.highlight_style,
- multi_select=args.multi_select,
- multi_select_cursor=args.multi_select_cursor,
- multi_select_cursor_brackets_style=args.multi_select_cursor_brackets_style,
- multi_select_cursor_style=args.multi_select_cursor_style,
- multi_select_empty_ok=args.multi_select_empty_ok,
- multi_select_keys=args.multi_select_keys,
- multi_select_select_on_accept=args.multi_select_select_on_accept,
- preselected_entries=args.preselected,
- preview_border=args.preview_border,
- preview_command=args.preview_command,
- preview_size=args.preview_size,
- preview_title=args.preview_title,
- search_case_sensitive=args.case_sensitive,
- search_highlight_style=args.search_highlight_style,
- search_key=args.search_key,
- shortcut_brackets_highlight_style=args.shortcut_brackets_highlight_style,
- shortcut_key_highlight_style=args.shortcut_key_highlight_style,
- show_multi_select_hint=args.show_multi_select_hint,
- show_multi_select_hint_text=args.show_multi_select_hint_text,
- show_search_hint=args.show_search_hint,
- show_search_hint_text=args.show_search_hint_text,
- show_shortcut_hints=args.show_shortcut_hints,
- show_shortcut_hints_in_status_bar=args.show_shortcut_hints_in_status_bar,
- skip_empty_entries=args.skip_empty_entries,
- status_bar=args.status_bar,
- status_bar_below_preview=args.status_bar_below_preview,
- status_bar_style=args.status_bar_style,
- title=args.title,
- )
- except (InvalidParameterCombinationError, InvalidStyleError, UnknownMenuEntryError) as e:
- print(str(e), file=sys.stderr)
- sys.exit(0)
- chosen_entries = terminal_menu.show()
- if chosen_entries is None:
- sys.exit(0)
- else:
- if isinstance(chosen_entries, Iterable):
- if args.stdout:
- print(",".join(str(entry + 1) for entry in chosen_entries))
- sys.exit(chosen_entries[0] + 1)
- else:
- chosen_entry = chosen_entries
- if args.stdout:
- print(chosen_entry + 1)
- sys.exit(chosen_entry + 1)
-
-
-if __name__ == "__main__":
- main()
diff --git a/archinstall/lib/menu/table_selection_menu.py b/archinstall/lib/menu/table_selection_menu.py
index 09cd6ee2..fec6ae59 100644
--- a/archinstall/lib/menu/table_selection_menu.py
+++ b/archinstall/lib/menu/table_selection_menu.py
@@ -1,19 +1,25 @@
-from typing import Any, Tuple, List, Dict, Optional
+from typing import Any, Tuple, List, Dict, Optional, Callable
-from .menu import MenuSelectionType, MenuSelection
+from .menu import MenuSelectionType, MenuSelection, Menu
from ..output import FormattedOutput
-from ..menu import Menu
class TableMenu(Menu):
def __init__(
self,
title: str,
- data: List[Any] = [],
+ data: Optional[List[Any]] = None,
table_data: Optional[Tuple[List[Any], str]] = None,
+ preset: List[Any] = [],
custom_menu_options: List[str] = [],
default: Any = None,
- multi: bool = False
+ multi: bool = False,
+ preview_command: Optional[Callable] = None,
+ preview_title: str = 'Info',
+ preview_size: float = 0.0,
+ allow_reset: bool = True,
+ allow_reset_warning_msg: Optional[str] = None,
+ skip: bool = True
):
"""
param title: Text that will be displayed above the menu
@@ -29,10 +35,10 @@ class TableMenu(Menu):
param custom_options: List of custom options that will be displayed under the table
:type custom_menu_options: List
- """
- if not data and not table_data:
- raise ValueError('Either "data" or "table_data" must be provided')
+ :param preview_command: A function that should return a string that will be displayed in a preview window when a menu selection item is in focus
+ :type preview_command: Callable
+ """
self._custom_options = custom_menu_options
self._multi = multi
@@ -41,7 +47,7 @@ class TableMenu(Menu):
else:
header_padding = 2
- if len(data):
+ if data is not None:
table_text = FormattedOutput.as_table(data)
rows = table_text.split('\n')
table = self._create_table(data, rows, header_padding=header_padding)
@@ -53,20 +59,54 @@ class TableMenu(Menu):
data = table_data[0]
rows = table_data[1].split('\n')
table = self._create_table(data, rows, header_padding=header_padding)
+ else:
+ raise ValueError('Either "data" or "table_data" must be provided')
self._options, header = self._prepare_selection(table)
+ preset_values = self._preset_values(preset)
+
+ extra_bottom_space = True if preview_command else False
+
super().__init__(
title,
self._options,
+ preset_values=preset_values,
header=header,
skip_empty_entries=True,
show_search_hint=False,
- allow_reset=True,
multi=multi,
- default_option=default
+ default_option=default,
+ preview_command=lambda x: self._table_show_preview(preview_command, x),
+ preview_size=preview_size,
+ preview_title=preview_title,
+ extra_bottom_space=extra_bottom_space,
+ allow_reset=allow_reset,
+ allow_reset_warning_msg=allow_reset_warning_msg,
+ skip=skip
)
+ def _preset_values(self, preset: List[Any]) -> List[str]:
+ # when we create the table of just the preset values it will
+ # be formatted a bit different due to spacing, so to determine
+ # correct rows lets remove all the spaces and compare apples with apples
+ preset_table = FormattedOutput.as_table(preset).strip()
+ data_rows = preset_table.split('\n')[2:] # get all data rows
+ pure_data_rows = [self._escape_row(row.replace(' ', '')) for row in data_rows]
+
+ # the actual preset value has to be in non-escaped form
+ pure_option_rows = {o.replace(' ', ''): self._unescape_row(o) for o in self._options.keys()}
+ preset_rows = [row for pure, row in pure_option_rows.items() if pure in pure_data_rows]
+
+ return preset_rows
+
+ def _table_show_preview(self, preview_command: Optional[Callable], selection: Any) -> Optional[str]:
+ if preview_command:
+ row = self._escape_row(selection)
+ obj = self._options[row]
+ return preview_command(obj)
+ return None
+
def run(self) -> MenuSelection:
choice = super().run()
@@ -79,6 +119,12 @@ class TableMenu(Menu):
return choice
+ def _escape_row(self, row: str) -> str:
+ return row.replace('|', '\\|')
+
+ def _unescape_row(self, row: str) -> str:
+ return row.replace('\\|', '|')
+
def _create_table(self, data: List[Any], rows: List[str], header_padding: int = 2) -> Dict[str, Any]:
# these are the header rows of the table and do not map to any data obviously
# we're adding 2 spaces as prefix because the menu selector '> ' will be put before
@@ -87,7 +133,7 @@ class TableMenu(Menu):
display_data = {f'{padding}{rows[0]}': None, f'{padding}{rows[1]}': None}
for row, entry in zip(rows[2:], data):
- row = row.replace('|', '\\|')
+ row = self._escape_row(row)
display_data[row] = entry
return display_data
diff --git a/archinstall/lib/menu/text_input.py b/archinstall/lib/menu/text_input.py
index 05ca0f22..971df5fd 100644
--- a/archinstall/lib/menu/text_input.py
+++ b/archinstall/lib/menu/text_input.py
@@ -1,4 +1,5 @@
import readline
+import sys
class TextInput:
@@ -12,6 +13,14 @@ class TextInput:
def run(self) -> str:
readline.set_pre_input_hook(self._hook)
- result = input(self._prompt)
+ try:
+ result = input(self._prompt)
+ except (KeyboardInterrupt, EOFError):
+ # To make sure any output that may follow
+ # will be on the line after the prompt
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+
+ result = ''
readline.set_pre_input_hook()
return result
diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py
index d76e0473..c9094669 100644
--- a/archinstall/lib/mirrors.py
+++ b/archinstall/lib/mirrors.py
@@ -1,187 +1,318 @@
-import logging
import pathlib
-import urllib.error
-import urllib.request
-from typing import Union, Mapping, Iterable, Dict, Any, List
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Dict, Any, List, Optional, TYPE_CHECKING
-from .general import SysCommand
-from .output import log
+from .menu import AbstractSubMenu, Selector, MenuSelectionType, Menu, ListManager, TextInput
+from .networking import fetch_data_from_url
+from .output import warn, FormattedOutput
from .storage import storage
-def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes:
- """
- This function can sort /etc/pacman.d/mirrorlist according to the
- mirror's URL prefix. By default places HTTPS before HTTP but it also
- preserves the country/rank-order.
-
- This assumes /etc/pacman.d/mirrorlist looks like the following:
-
- ## Comment
- Server = url
-
- or
-
- ## Comment
- #Server = url
-
- But the Comments need to start with double-hashmarks to be distringuished
- from server url definitions (commented or uncommented).
- """
- comments_and_whitespaces = b""
-
- categories = {key: [] for key in sort_order + ["Unknown"]}
- for line in raw_data.split(b"\n"):
- if line[0:2] in (b'##', b''):
- comments_and_whitespaces += line + b'\n'
- elif line[:6].lower() == b'server' or line[:7].lower() == b'#server':
- opening, url = line.split(b'=', 1)
- opening, url = opening.strip(), url.strip()
- if (category := url.split(b'://',1)[0].decode('UTF-8')) in categories:
- categories[category].append(comments_and_whitespaces)
- categories[category].append(opening + b' = ' + url + b'\n')
- else:
- categories["Unknown"].append(comments_and_whitespaces)
- categories["Unknown"].append(opening + b' = ' + url + b'\n')
-
- comments_and_whitespaces = b""
-
- new_raw_data = b''
- for category in sort_order + ["Unknown"]:
- for line in categories[category]:
- new_raw_data += line
-
- return new_raw_data
-
-
-def filter_mirrors_by_region(regions :str,
- destination :str = '/etc/pacman.d/mirrorlist',
- sort_order :List[str] = ["https", "http"],
- *args :str,
- **kwargs :str
-) -> Union[bool, bytes]:
+if TYPE_CHECKING:
+ _: Any
+
+
+class SignCheck(Enum):
+ Never = 'Never'
+ Optional = 'Optional'
+ Required = 'Required'
+
+
+class SignOption(Enum):
+ TrustedOnly = 'TrustedOnly'
+ TrustAll = 'TrustAll'
+
+
+@dataclass
+class CustomMirror:
+ name: str
+ url: str
+ sign_check: SignCheck
+ sign_option: SignOption
+
+ def table_data(self) -> Dict[str, str]:
+ return {
+ 'Name': self.name,
+ 'Url': self.url,
+ 'Sign check': self.sign_check.value,
+ 'Sign options': self.sign_option.value
+ }
+
+ def json(self) -> Dict[str, str]:
+ return {
+ 'name': self.name,
+ 'url': self.url,
+ 'sign_check': self.sign_check.value,
+ 'sign_option': self.sign_option.value
+ }
+
+ @classmethod
+ def parse_args(cls, args: List[Dict[str, str]]) -> List['CustomMirror']:
+ configs = []
+ for arg in args:
+ configs.append(
+ CustomMirror(
+ arg['name'],
+ arg['url'],
+ SignCheck(arg['sign_check']),
+ SignOption(arg['sign_option'])
+ )
+ )
+
+ return configs
+
+
+@dataclass
+class MirrorConfiguration:
+ mirror_regions: Dict[str, List[str]] = field(default_factory=dict)
+ custom_mirrors: List[CustomMirror] = field(default_factory=list)
+
+ @property
+ def regions(self) -> str:
+ return ', '.join(self.mirror_regions.keys())
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'mirror_regions': self.mirror_regions,
+ 'custom_mirrors': [c.json() for c in self.custom_mirrors]
+ }
+
+ def mirrorlist_config(self) -> str:
+ config = ''
+
+ for region, mirrors in self.mirror_regions.items():
+ for mirror in mirrors:
+ config += f'\n\n## {region}\nServer = {mirror}\n'
+
+ for cm in self.custom_mirrors:
+ config += f'\n\n## {cm.name}\nServer = {cm.url}\n'
+
+ return config
+
+ def pacman_config(self) -> str:
+ config = ''
+
+ for mirror in self.custom_mirrors:
+ config += f'\n\n[{mirror.name}]\n'
+ config += f'SigLevel = {mirror.sign_check.value} {mirror.sign_option.value}\n'
+ config += f'Server = {mirror.url}\n'
+
+ return config
+
+ @classmethod
+ def parse_args(cls, args: Dict[str, Any]) -> 'MirrorConfiguration':
+ config = MirrorConfiguration()
+
+ if 'mirror_regions' in args:
+ config.mirror_regions = args['mirror_regions']
+
+ if 'custom_mirrors' in args:
+ config.custom_mirrors = CustomMirror.parse_args(args['custom_mirrors'])
+
+ return config
+
+
+class CustomMirrorList(ListManager):
+ def __init__(self, prompt: str, custom_mirrors: List[CustomMirror]):
+ self._actions = [
+ str(_('Add a custom mirror')),
+ str(_('Change custom mirror')),
+ str(_('Delete custom mirror'))
+ ]
+ super().__init__(prompt, custom_mirrors, [self._actions[0]], self._actions[1:])
+
+ def selected_action_display(self, mirror: CustomMirror) -> str:
+ return mirror.name
+
+ def handle_action(
+ self,
+ action: str,
+ entry: Optional[CustomMirror],
+ data: List[CustomMirror]
+ ) -> List[CustomMirror]:
+ if action == self._actions[0]: # add
+ new_mirror = self._add_custom_mirror()
+ if new_mirror is not None:
+ data = [d for d in data if d.name != new_mirror.name]
+ data += [new_mirror]
+ elif action == self._actions[1] and entry: # modify mirror
+ new_mirror = self._add_custom_mirror(entry)
+ if new_mirror is not None:
+ data = [d for d in data if d.name != entry.name]
+ data += [new_mirror]
+ elif action == self._actions[2] and entry: # delete
+ data = [d for d in data if d != entry]
+
+ return data
+
+ def _add_custom_mirror(self, mirror: Optional[CustomMirror] = None) -> Optional[CustomMirror]:
+ prompt = '\n\n' + str(_('Enter name (leave blank to skip): '))
+ existing_name = mirror.name if mirror else ''
+
+ while True:
+ name = TextInput(prompt, existing_name).run()
+ if not name:
+ return mirror
+ break
+
+ prompt = '\n' + str(_('Enter url (leave blank to skip): '))
+ existing_url = mirror.url if mirror else ''
+
+ while True:
+ url = TextInput(prompt, existing_url).run()
+ if not url:
+ return mirror
+ break
+
+ sign_check_choice = Menu(
+ str(_('Select signature check option')),
+ [s.value for s in SignCheck],
+ skip=False,
+ clear_screen=False,
+ preset_values=mirror.sign_check.value if mirror else None
+ ).run()
+
+ sign_option_choice = Menu(
+ str(_('Select signature option')),
+ [s.value for s in SignOption],
+ skip=False,
+ clear_screen=False,
+ preset_values=mirror.sign_option.value if mirror else None
+ ).run()
+
+ return CustomMirror(
+ name,
+ url,
+ SignCheck(sign_check_choice.single_value),
+ SignOption(sign_option_choice.single_value)
+ )
+
+
+class MirrorMenu(AbstractSubMenu):
+ def __init__(
+ self,
+ data_store: Dict[str, Any],
+ preset: Optional[MirrorConfiguration] = None
+ ):
+ if preset:
+ self._preset = preset
+ else:
+ self._preset = MirrorConfiguration()
+
+ super().__init__(data_store=data_store)
+
+ def setup_selection_menu_options(self):
+ self._menu_options['mirror_regions'] = \
+ Selector(
+ _('Mirror region'),
+ lambda preset: select_mirror_regions(preset),
+ display_func=lambda x: ', '.join(x.keys()) if x else '',
+ default=self._preset.mirror_regions,
+ enabled=True)
+ self._menu_options['custom_mirrors'] = \
+ Selector(
+ _('Custom mirrors'),
+ lambda preset: select_custom_mirror(preset=preset),
+ display_func=lambda x: str(_('Defined')) if x else '',
+ preview_func=self._prev_custom_mirror,
+ default=self._preset.custom_mirrors,
+ enabled=True
+ )
+
+ def _prev_custom_mirror(self) -> Optional[str]:
+ selector = self._menu_options['custom_mirrors']
+
+ if selector.has_selection():
+ custom_mirrors: List[CustomMirror] = selector.current_selection # type: ignore
+ output = FormattedOutput.as_table(custom_mirrors)
+ return output.strip()
+
+ return None
+
+ def run(self, allow_reset: bool = True) -> Optional[MirrorConfiguration]:
+ super().run(allow_reset=allow_reset)
+
+ if self._data_store.get('mirror_regions', None) or self._data_store.get('custom_mirrors', None):
+ return MirrorConfiguration(
+ mirror_regions=self._data_store['mirror_regions'],
+ custom_mirrors=self._data_store['custom_mirrors'],
+ )
+
+ return None
+
+
+def select_mirror_regions(preset_values: Dict[str, List[str]] = {}) -> Dict[str, List[str]]:
"""
- This function will change the active mirrors on the live medium by
- filtering which regions are active based on `regions`.
+ Asks the user to select a mirror or region
+ Usually this is combined with :ref:`archinstall.list_mirrors`.
- :param regions: A series of country codes separated by `,`. For instance `SE,US` for sweden and United States.
- :type regions: str
+ :return: The dictionary information about a mirror/region.
+ :rtype: dict
"""
- region_list = [f'country={region}' for region in regions.split(',')]
- response = urllib.request.urlopen(urllib.request.Request(f"https://archlinux.org/mirrorlist/?{'&'.join(region_list)}&protocol=https&protocol=http&ip_version=4&ip_version=6&use_mirror_status=on'", headers={'User-Agent': 'ArchInstall'}))
- new_list = response.read().replace(b"#Server", b"Server")
-
- if sort_order:
- new_list = sort_mirrorlist(new_list, sort_order=sort_order)
+ if preset_values is None:
+ preselected = None
+ else:
+ preselected = list(preset_values.keys())
- if destination:
- with open(destination, "wb") as mirrorlist:
- mirrorlist.write(new_list)
+ mirrors = list_mirrors()
- return True
- else:
- return new_list.decode('UTF-8')
+ choice = Menu(
+ _('Select one of the regions to download packages from'),
+ list(mirrors.keys()),
+ preset_values=preselected,
+ multi=True,
+ allow_reset=True
+ ).run()
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return {}
+ case MenuSelectionType.Skip:
+ return preset_values
+ case MenuSelectionType.Selection:
+ return {selected: mirrors[selected] for selected in choice.multi_value}
-def add_custom_mirrors(mirrors: List[str], *args :str, **kwargs :str) -> bool:
- """
- This will append custom mirror definitions in pacman.conf
+ return {}
- :param mirrors: A list of mirror data according to: `{'url': 'http://url.com', 'signcheck': 'Optional', 'signoptions': 'TrustAll', 'name': 'testmirror'}`
- :type mirrors: dict
- """
- with open('/etc/pacman.conf', 'a') as pacman:
- for mirror in mirrors:
- pacman.write(f"[{mirror['name']}]\n")
- pacman.write(f"SigLevel = {mirror['signcheck']} {mirror['signoptions']}\n")
- pacman.write(f"Server = {mirror['url']}\n")
- return True
+def select_custom_mirror(prompt: str = '', preset: List[CustomMirror] = []):
+ custom_mirrors = CustomMirrorList(prompt, preset).run()
+ return custom_mirrors
-def insert_mirrors(mirrors :Dict[str, Any], *args :str, **kwargs :str) -> bool:
- """
- This function will insert a given mirror-list at the top of `/etc/pacman.d/mirrorlist`.
- It will not flush any other mirrors, just insert new ones.
+def _parse_mirror_list(mirrorlist: str) -> Dict[str, List[str]]:
+ file_content = mirrorlist.split('\n')
+ file_content = list(filter(lambda x: x, file_content)) # filter out empty lines
+ first_srv_idx = [idx for idx, line in enumerate(file_content) if 'server' in line.lower()][0]
+ mirrors = file_content[first_srv_idx - 1:]
- :param mirrors: A dictionary of `{'url' : 'country', 'url2' : 'country'}`
- :type mirrors: dict
- """
- original_mirrorlist = ''
- with open('/etc/pacman.d/mirrorlist', 'r') as original:
- original_mirrorlist = original.read()
-
- with open('/etc/pacman.d/mirrorlist', 'w') as new_mirrorlist:
- for mirror, country in mirrors.items():
- new_mirrorlist.write(f'## {country}\n')
- new_mirrorlist.write(f'Server = {mirror}\n')
- new_mirrorlist.write('\n')
- new_mirrorlist.write(original_mirrorlist)
-
- return True
-
-
-def use_mirrors(
- regions: Mapping[str, Iterable[str]],
- destination: str = '/etc/pacman.d/mirrorlist'
-) -> None:
- log(f'A new package mirror-list has been created: {destination}', level=logging.INFO)
- with open(destination, 'w') as mirrorlist:
- for region, mirrors in regions.items():
- for mirror in mirrors:
- mirrorlist.write(f'## {region}\n')
- mirrorlist.write(f'Server = {mirror}\n')
+ mirror_list: Dict[str, List[str]] = {}
+ for idx in range(0, len(mirrors), 2):
+ region = mirrors[idx].removeprefix('## ')
+ url = mirrors[idx + 1].removeprefix('#').removeprefix('Server = ')
+ mirror_list.setdefault(region, []).append(url)
-def re_rank_mirrors(
- top: int = 10,
- src: str = '/etc/pacman.d/mirrorlist',
- dst: str = '/etc/pacman.d/mirrorlist',
-) -> bool:
- cmd = SysCommand(f"/usr/bin/rankmirrors -n {top} {src}")
- if cmd.exit_code != 0:
- return False
- with open(dst, 'w') as f:
- f.write(str(cmd))
- return True
+ return mirror_list
-def list_mirrors(sort_order :List[str] = ["https", "http"]) -> Dict[str, Any]:
- regions = {}
+def list_mirrors() -> Dict[str, List[str]]:
+ regions: Dict[str, List[str]] = {}
if storage['arguments']['offline']:
- with pathlib.Path('/etc/pacman.d/mirrorlist').open('rb') as fh:
- mirrorlist = fh.read()
+ with pathlib.Path('/etc/pacman.d/mirrorlist').open('r') as fp:
+ mirrorlist = fp.read()
else:
url = "https://archlinux.org/mirrorlist/?protocol=https&protocol=http&ip_version=4&ip_version=6&use_mirror_status=on"
-
try:
- response = urllib.request.urlopen(url)
- except urllib.error.URLError as err:
- log(f'Could not fetch an active mirror-list: {err}', level=logging.WARNING, fg="orange")
+ mirrorlist = fetch_data_from_url(url)
+ except ValueError as err:
+ warn(f'Could not fetch an active mirror-list: {err}')
return regions
- mirrorlist = response.read()
-
- if sort_order:
- mirrorlist = sort_mirrorlist(mirrorlist, sort_order=sort_order)
-
- region = 'Unknown region'
- for line in mirrorlist.split(b'\n'):
- if len(line.strip()) == 0:
- continue
-
- line = line.decode('UTF-8').strip('\n').strip('\r')
- if line[:3] == '## ':
- region = line[3:]
- elif line[:10] == '#Server = ':
- regions.setdefault(region, {})
-
- url = line.lstrip('#Server = ')
- regions[region][url] = True
- elif line.startswith('Server = '):
- regions.setdefault(region, {})
-
- url = line.lstrip('Server = ')
- regions[region][url] = True
+ regions = _parse_mirror_list(mirrorlist)
+ sorted_regions = {}
+ for region, urls in regions.items():
+ sorted_regions[region] = sorted(urls, reverse=True)
- return regions
+ return sorted_regions
diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py
index 4a018b2c..a1c90e48 100644
--- a/archinstall/lib/models/__init__.py
+++ b/archinstall/lib/models/__init__.py
@@ -1 +1,9 @@
-from .network_configuration import NetworkConfiguration as NetworkConfiguration \ No newline at end of file
+from .network_configuration import (
+ NetworkConfiguration,
+ NicType,
+ Nic
+)
+from .bootloader import Bootloader
+from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage
+from .users import PasswordStrength, User
+from .audio_configuration import Audio, AudioConfiguration
diff --git a/archinstall/lib/models/audio_configuration.py b/archinstall/lib/models/audio_configuration.py
new file mode 100644
index 00000000..88cd5d8e
--- /dev/null
+++ b/archinstall/lib/models/audio_configuration.py
@@ -0,0 +1,54 @@
+from dataclasses import dataclass
+from enum import Enum
+from typing import Any, TYPE_CHECKING, Dict
+
+from ..hardware import SysInfo
+from ..output import info
+from ...default_profiles.applications.pipewire import PipewireProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+@dataclass
+class Audio(Enum):
+ Pipewire = 'pipewire'
+ Pulseaudio = 'pulseaudio'
+
+ @staticmethod
+ def no_audio_text() -> str:
+ return str(_('No audio server'))
+
+
+@dataclass
+class AudioConfiguration:
+ audio: Audio
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'audio': self.audio.value
+ }
+
+ @staticmethod
+ def parse_arg(arg: Dict[str, Any]) -> 'AudioConfiguration':
+ return AudioConfiguration(
+ Audio(arg['audio'])
+ )
+
+ def install_audio_config(
+ self,
+ installation: Any
+ ):
+ info(f'Installing audio server: {self.audio.name}')
+
+ match self.audio:
+ case Audio.Pipewire:
+ PipewireProfile().install(installation)
+ case Audio.Pulseaudio:
+ installation.add_additional_packages("pulseaudio")
+
+ if SysInfo.requires_sof_fw():
+ installation.add_additional_packages('sof-firmware')
+
+ if SysInfo.requires_alsa_fw():
+ installation.add_additional_packages('alsa-firmware')
diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py
new file mode 100644
index 00000000..aa1a8e27
--- /dev/null
+++ b/archinstall/lib/models/bootloader.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import sys
+from enum import Enum
+from typing import List
+
+from ..hardware import SysInfo
+from ..output import warn
+
+
+class Bootloader(Enum):
+ Systemd = 'Systemd-boot'
+ Grub = 'Grub'
+ Efistub = 'Efistub'
+ Limine = 'Limine'
+
+ def has_uki_support(self) -> bool:
+ match self:
+ case Bootloader.Efistub | Bootloader.Systemd:
+ return True
+ case _:
+ return False
+
+ def json(self) -> str:
+ return self.value
+
+ @staticmethod
+ def values() -> List[str]:
+ return [e.value for e in Bootloader]
+
+ @classmethod
+ def get_default(cls) -> Bootloader:
+ if SysInfo.has_uefi():
+ return Bootloader.Systemd
+ else:
+ return Bootloader.Grub
+
+ @classmethod
+ def from_arg(cls, bootloader: str) -> Bootloader:
+ # to support old configuration files
+ bootloader = bootloader.capitalize()
+
+ if bootloader not in cls.values():
+ values = ', '.join(cls.values())
+ warn(f'Invalid bootloader value "{bootloader}". Allowed values: {values}')
+ sys.exit(1)
+ return Bootloader(bootloader)
diff --git a/archinstall/lib/models/disk_encryption.py b/archinstall/lib/models/disk_encryption.py
deleted file mode 100644
index a4a501d9..00000000
--- a/archinstall/lib/models/disk_encryption.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from __future__ import annotations
-
-from dataclasses import dataclass, field
-from enum import Enum
-from typing import Optional, List, Dict, TYPE_CHECKING, Any
-
-from ..hsm.fido import Fido2Device
-
-if TYPE_CHECKING:
- _: Any
-
-
-class EncryptionType(Enum):
- Partition = 'partition'
-
- @classmethod
- def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']:
- return {
- # str(_('Full disk encryption')): EncryptionType.FullDiskEncryption,
- str(_('Partition encryption')): EncryptionType.Partition
- }
-
- @classmethod
- def text_to_type(cls, text: str) -> 'EncryptionType':
- mapping = cls._encryption_type_mapper()
- return mapping[text]
-
- @classmethod
- def type_to_text(cls, type_: 'EncryptionType') -> str:
- mapping = cls._encryption_type_mapper()
- type_to_text = {type_: text for text, type_ in mapping.items()}
- return type_to_text[type_]
-
-
-@dataclass
-class DiskEncryption:
- encryption_type: EncryptionType = EncryptionType.Partition
- encryption_password: str = ''
- partitions: Dict[str, List[Dict[str, Any]]] = field(default_factory=dict)
- hsm_device: Optional[Fido2Device] = None
-
- @property
- def all_partitions(self) -> List[Dict[str, Any]]:
- _all: List[Dict[str, Any]] = []
- for parts in self.partitions.values():
- _all += parts
- return _all
-
- def generate_encryption_file(self, partition) -> bool:
- return partition in self.all_partitions and partition['mountpoint'] != '/'
-
- def json(self) -> Dict[str, Any]:
- obj = {
- 'encryption_type': self.encryption_type.value,
- 'partitions': self.partitions
- }
-
- if self.hsm_device:
- obj['hsm_device'] = self.hsm_device.json()
-
- return obj
-
- @classmethod
- def parse_arg(
- cls,
- disk_layout: Dict[str, Any],
- arg: Dict[str, Any],
- password: str = ''
- ) -> 'DiskEncryption':
- # we have to map the enc partition config to the disk layout objects
- # they both need to point to the same object as it will get modified
- # during the installation process
- enc_partitions: Dict[str, List[Dict[str, Any]]] = {}
-
- for path, partitions in disk_layout.items():
- conf_partitions = arg['partitions'].get(path, [])
- for part in partitions['partitions']:
- if part in conf_partitions:
- enc_partitions.setdefault(path, []).append(part)
-
- enc = DiskEncryption(
- EncryptionType(arg['encryption_type']),
- password,
- enc_partitions
- )
-
- if hsm := arg.get('hsm_device', None):
- enc.hsm_device = Fido2Device.parse_arg(hsm)
-
- return enc
diff --git a/archinstall/lib/models/dataclasses.py b/archinstall/lib/models/gen.py
index 99221fe3..fb7e5751 100644
--- a/archinstall/lib/models/dataclasses.py
+++ b/archinstall/lib/models/gen.py
@@ -1,16 +1,17 @@
from dataclasses import dataclass
-from typing import Optional, List
+from typing import Optional, List, Dict, Any
+
@dataclass
class VersionDef:
version_string: str
@classmethod
- def parse_version(self) -> List[str]:
- if '.' in self.version_string:
- versions = self.version_string.split('.')
+ def parse_version(cls) -> List[str]:
+ if '.' in cls.version_string:
+ versions = cls.version_string.split('.')
else:
- versions = [self.version_string]
+ versions = [cls.version_string]
return versions
@@ -19,37 +20,44 @@ class VersionDef:
return self.parse_version()[0]
@classmethod
- def minor(self) -> str:
- versions = self.parse_version()
+ def minor(cls) -> Optional[str]:
+ versions = cls.parse_version()
if len(versions) >= 2:
return versions[1]
+ return None
+
@classmethod
- def patch(self) -> str:
- versions = self.parse_version()
+ def patch(cls) -> Optional[str]:
+ versions = cls.parse_version()
if '-' in versions[-1]:
_, patch_version = versions[-1].split('-', 1)
return patch_version
- def __eq__(self, other :'VersionDef') -> bool:
+ return None
+
+ def __eq__(self, other) -> bool:
if other.major == self.major and \
other.minor == self.minor and \
other.patch == self.patch:
return True
return False
-
- def __lt__(self, other :'VersionDef') -> bool:
- if self.major > other.major:
+
+ def __lt__(self, other) -> bool:
+ if self.major() > other.major():
return False
- elif self.minor and other.minor and self.minor > other.minor:
+ elif self.minor() and other.minor() and self.minor() > other.minor():
return False
- elif self.patch and other.patch and self.patch > other.patch:
+ elif self.patch() and other.patch() and self.patch() > other.patch():
return False
+ return True
+
def __str__(self) -> str:
return self.version_string
+
@dataclass
class PackageSearchResult:
pkgname: str
@@ -79,16 +87,21 @@ class PackageSearchResult:
makedepends: List[str]
checkdepends: List[str]
+ @staticmethod
+ def from_json(data: Dict[str, Any]) -> 'PackageSearchResult':
+ return PackageSearchResult(**data)
+
@property
def pkg_version(self) -> str:
return self.pkgver
- def __eq__(self, other :'VersionDef') -> bool:
+ def __eq__(self, other) -> bool:
return self.pkg_version == other.pkg_version
- def __lt__(self, other :'VersionDef') -> bool:
+ def __lt__(self, other) -> bool:
return self.pkg_version < other.pkg_version
+
@dataclass
class PackageSearch:
version: int
@@ -98,8 +111,19 @@ class PackageSearch:
page: int
results: List[PackageSearchResult]
- def __post_init__(self):
- self.results = [PackageSearchResult(**x) for x in self.results]
+ @staticmethod
+ def from_json(data: Dict[str, Any]) -> 'PackageSearch':
+ results = [PackageSearchResult.from_json(r) for r in data['results']]
+
+ return PackageSearch(
+ version=data['version'],
+ limit=data['limit'],
+ valid=data['valid'],
+ num_pages=data['num_pages'],
+ page=data['page'],
+ results=results
+ )
+
@dataclass
class LocalPackage:
@@ -129,8 +153,8 @@ class LocalPackage:
def pkg_version(self) -> str:
return self.version
- def __eq__(self, other :'VersionDef') -> bool:
+ def __eq__(self, other) -> bool:
return self.pkg_version == other.pkg_version
- def __lt__(self, other :'VersionDef') -> bool:
- return self.pkg_version < other.pkg_version \ No newline at end of file
+ def __lt__(self, other) -> bool:
+ return self.pkg_version < other.pkg_version
diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py
index e026e97b..dfd8b8cb 100644
--- a/archinstall/lib/models/network_configuration.py
+++ b/archinstall/lib/models/network_configuration.py
@@ -1,183 +1,144 @@
from __future__ import annotations
-from dataclasses import dataclass
+from dataclasses import dataclass, field
from enum import Enum
-from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING
+from typing import List, Optional, Dict, Any, TYPE_CHECKING, Tuple
-from ..output import log
-from ..storage import storage
+from ..profile import ProfileConfiguration
if TYPE_CHECKING:
_: Any
-class NicType(str, Enum):
+class NicType(Enum):
ISO = "iso"
NM = "nm"
MANUAL = "manual"
+ def display_msg(self) -> str:
+ match self:
+ case NicType.ISO:
+ return str(_('Copy ISO network configuration to installation'))
+ case NicType.NM:
+ return str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE Plasma)'))
+ case NicType.MANUAL:
+ return str(_('Manual configuration'))
+
@dataclass
-class NetworkConfiguration:
- type: NicType
+class Nic:
iface: Optional[str] = None
ip: Optional[str] = None
dhcp: bool = True
gateway: Optional[str] = None
- dns: Union[None, List[str]] = None
-
- def __str__(self):
- if self.is_iso():
- return "Copy ISO configuration"
- elif self.is_network_manager():
- return "Use NetworkManager"
- elif self.is_manual():
- if self.dhcp:
- return f'iface={self.iface}, dhcp=auto'
- else:
- return f'iface={self.iface}, ip={self.ip}, dhcp=staticIp, gateway={self.gateway}, dns={self.dns}'
+ dns: List[str] = field(default_factory=list)
+
+ def table_data(self) -> Dict[str, Any]:
+ return {
+ 'iface': self.iface if self.iface else '',
+ 'ip': self.ip if self.ip else '',
+ 'dhcp': self.dhcp,
+ 'gateway': self.gateway if self.gateway else '',
+ 'dns': self.dns
+ }
+
+ def json(self) -> Dict[str, Any]:
+ return {
+ 'iface': self.iface,
+ 'ip': self.ip,
+ 'dhcp': self.dhcp,
+ 'gateway': self.gateway,
+ 'dns': self.dns
+ }
+
+ @staticmethod
+ def parse_arg(arg: Dict[str, Any]) -> Nic:
+ return Nic(
+ iface=arg.get('iface', None),
+ ip=arg.get('ip', None),
+ dhcp=arg.get('dhcp', True),
+ gateway=arg.get('gateway', None),
+ dns=arg.get('dns', []),
+ )
+
+ def as_systemd_config(self) -> str:
+ match: List[Tuple[str, str]] = []
+ network: List[Tuple[str, str]] = []
+
+ if self.iface:
+ match.append(('Name', self.iface))
+
+ if self.dhcp:
+ network.append(('DHCP', 'yes'))
else:
- return 'Unknown type'
-
- def as_json(self) -> Dict:
- exclude_fields = ['type']
- data = {}
- for k, v in self.__dict__.items():
- if k not in exclude_fields:
- if isinstance(v, list) and len(v) == 0:
- v = ''
- elif v is None:
- v = ''
-
- data[k] = v
-
- return data
+ if self.ip:
+ network.append(('Address', self.ip))
+ if self.gateway:
+ network.append(('Gateway', self.gateway))
+ for dns in self.dns:
+ network.append(('DNS', dns))
- def json(self) -> Dict:
- # for json serialization when calling json.dumps(...) on this class
- return self.__dict__
+ config = {'Match': match, 'Network': network}
- def is_iso(self) -> bool:
- return self.type == NicType.ISO
+ config_str = ''
+ for top, entries in config.items():
+ config_str += f'[{top}]\n'
+ config_str += '\n'.join([f'{k}={v}' for k, v in entries])
+ config_str += '\n\n'
- def is_network_manager(self) -> bool:
- return self.type == NicType.NM
+ return config_str
- def is_manual(self) -> bool:
- return self.type == NicType.MANUAL
+@dataclass
+class NetworkConfiguration:
+ type: NicType
+ nics: List[Nic] = field(default_factory=list)
-class NetworkConfigurationHandler:
- def __init__(self, config: Union[None, NetworkConfiguration, List[NetworkConfiguration]] = None):
- self._configuration = config
-
- @property
- def configuration(self):
- return self._configuration
+ def json(self) -> Dict[str, Any]:
+ config: Dict[str, Any] = {'type': self.type.value}
+ if self.nics:
+ config['nics'] = [n.json() for n in self.nics]
- def config_installer(self, installation: Any):
- if self._configuration is None:
- return
+ return config
- if isinstance(self._configuration, list):
- for config in self._configuration:
- installation.configure_nic(config)
+ @staticmethod
+ def parse_arg(config: Dict[str, Any]) -> Optional[NetworkConfiguration]:
+ nic_type = config.get('type', None)
+ if not nic_type:
+ return None
- installation.enable_service('systemd-networkd')
- installation.enable_service('systemd-resolved')
- else:
- # If user selected to copy the current ISO network configuration
- # Perform a copy of the config
- if self._configuration.is_iso():
+ match NicType(nic_type):
+ case NicType.ISO:
+ return NetworkConfiguration(NicType.ISO)
+ case NicType.NM:
+ return NetworkConfiguration(NicType.NM)
+ case NicType.MANUAL:
+ nics_arg = config.get('nics', [])
+ if nics_arg:
+ nics = [Nic.parse_arg(n) for n in nics_arg]
+ return NetworkConfiguration(NicType.MANUAL, nics)
+
+ return None
+
+ def install_network_config(
+ self,
+ installation: Any,
+ profile_config: Optional[ProfileConfiguration] = None
+ ):
+ match self.type:
+ case NicType.ISO:
installation.copy_iso_network_config(
- enable_services=True) # Sources the ISO network configuration to the install medium.
- elif self._configuration.is_network_manager():
+ enable_services=True # Sources the ISO network configuration to the install medium.
+ )
+ case NicType.NM:
installation.add_additional_packages(["networkmanager"])
- if (profile := storage['arguments'].get('profile')) and profile.is_desktop_profile:
- installation.add_additional_packages(["network-manager-applet"])
+ if profile_config and profile_config.profile:
+ if profile_config.profile.is_desktop_profile():
+ installation.add_additional_packages(["network-manager-applet"])
installation.enable_service('NetworkManager.service')
+ case NicType.MANUAL:
+ for nic in self.nics:
+ installation.configure_nic(nic)
- def _backwards_compability_config(self, config: Union[str,Dict[str, str]]) -> Union[List[NetworkConfiguration], NetworkConfiguration, None]:
- def get(config: Dict[str, str], key: str) -> List[str]:
- if (value := config.get(key, None)) is not None:
- return [value]
- return []
-
- if isinstance(config, str): # is a ISO network
- return NetworkConfiguration(NicType.ISO)
- elif config.get('NetworkManager'): # is a network manager configuration
- return NetworkConfiguration(NicType.NM)
- elif 'ip' in config:
- return [NetworkConfiguration(
- NicType.MANUAL,
- iface=config.get('nic', ''),
- ip=config.get('ip'),
- gateway=config.get('gateway', ''),
- dns=get(config, 'dns'),
- dhcp=False
- )]
- elif 'nic' in config:
- return [NetworkConfiguration(
- NicType.MANUAL,
- iface=config.get('nic', ''),
- dhcp=True
- )]
- else: # not recognized
- return None
-
- def _parse_manual_config(self, configs: List[Dict[str, Any]]) -> Optional[List[NetworkConfiguration]]:
- configurations = []
-
- for manual_config in configs:
- iface = manual_config.get('iface', None)
-
- if iface is None:
- log(_('No iface specified for manual configuration'))
- exit(1)
-
- if manual_config.get('dhcp', False) or not any([manual_config.get(v, '') for v in ['ip', 'gateway', 'dns']]):
- configurations.append(
- NetworkConfiguration(NicType.MANUAL, iface=iface)
- )
- else:
- ip = manual_config.get('ip', '')
- if not ip:
- log(_('Manual nic configuration with no auto DHCP requires an IP address'), fg='red')
- exit(1)
-
- configurations.append(
- NetworkConfiguration(
- NicType.MANUAL,
- iface=iface,
- ip=ip,
- gateway=manual_config.get('gateway', ''),
- dns=manual_config.get('dns', []),
- dhcp=False
- )
- )
-
- return configurations
-
- def _parse_nic_type(self, nic_type: str) -> NicType:
- try:
- return NicType(nic_type)
- except ValueError:
- options = [e.value for e in NicType]
- log(_('Unknown nic type: {}. Possible values are {}').format(nic_type, options), fg='red')
- exit(1)
-
- def parse_arguments(self, config: Any):
- if isinstance(config, list): # new data format
- self._configuration = self._parse_manual_config(config)
- elif nic_type := config.get('type', None): # new data format
- type_ = self._parse_nic_type(nic_type)
-
- if type_ != NicType.MANUAL:
- self._configuration = NetworkConfiguration(type_)
- else: # manual configuration settings
- self._configuration = self._parse_manual_config([config])
- else: # old style definitions
- network_config = self._backwards_compability_config(config)
- if network_config:
- return network_config
- return None
+ installation.enable_service('systemd-networkd')
+ installation.enable_service('systemd-resolved')
diff --git a/archinstall/lib/models/password_strength.py b/archinstall/lib/models/password_strength.py
deleted file mode 100644
index 61986bf0..00000000
--- a/archinstall/lib/models/password_strength.py
+++ /dev/null
@@ -1,85 +0,0 @@
-from enum import Enum
-
-
-class PasswordStrength(Enum):
- VERY_WEAK = 'very weak'
- WEAK = 'weak'
- MODERATE = 'moderate'
- STRONG = 'strong'
-
- @property
- def value(self):
- match self:
- case PasswordStrength.VERY_WEAK: return str(_('very weak'))
- case PasswordStrength.WEAK: return str(_('weak'))
- case PasswordStrength.MODERATE: return str(_('moderate'))
- case PasswordStrength.STRONG: return str(_('strong'))
-
- def color(self):
- match self:
- case PasswordStrength.VERY_WEAK: return 'red'
- case PasswordStrength.WEAK: return 'red'
- case PasswordStrength.MODERATE: return 'yellow'
- case PasswordStrength.STRONG: return 'green'
-
- @classmethod
- def strength(cls, password: str) -> 'PasswordStrength':
- digit = any(character.isdigit() for character in password)
- upper = any(character.isupper() for character in password)
- lower = any(character.islower() for character in password)
- symbol = any(not character.isalnum() for character in password)
- return cls._check_password_strength(digit, upper, lower, symbol, len(password))
-
- @classmethod
- def _check_password_strength(
- cls,
- digit: bool,
- upper: bool,
- lower: bool,
- symbol: bool,
- length: int
- ) -> 'PasswordStrength':
- # suggested evaluation
- # https://github.com/archlinux/archinstall/issues/1304#issuecomment-1146768163
- if digit and upper and lower and symbol:
- match length:
- case num if 13 <= num:
- return PasswordStrength.STRONG
- case num if 11 <= num <= 12:
- return PasswordStrength.MODERATE
- case num if 7 <= num <= 10:
- return PasswordStrength.WEAK
- case num if num <= 6:
- return PasswordStrength.VERY_WEAK
- elif digit and upper and lower:
- match length:
- case num if 14 <= num:
- return PasswordStrength.STRONG
- case num if 11 <= num <= 13:
- return PasswordStrength.MODERATE
- case num if 7 <= num <= 10:
- return PasswordStrength.WEAK
- case num if num <= 6:
- return PasswordStrength.VERY_WEAK
- elif upper and lower:
- match length:
- case num if 15 <= num:
- return PasswordStrength.STRONG
- case num if 12 <= num <= 14:
- return PasswordStrength.MODERATE
- case num if 7 <= num <= 11:
- return PasswordStrength.WEAK
- case num if num <= 6:
- return PasswordStrength.VERY_WEAK
- elif lower or upper:
- match length:
- case num if 18 <= num:
- return PasswordStrength.STRONG
- case num if 14 <= num <= 17:
- return PasswordStrength.MODERATE
- case num if 9 <= num <= 13:
- return PasswordStrength.WEAK
- case num if num <= 8:
- return PasswordStrength.VERY_WEAK
-
- return PasswordStrength.VERY_WEAK
diff --git a/archinstall/lib/models/pydantic.py b/archinstall/lib/models/pydantic.py
deleted file mode 100644
index 799e92af..00000000
--- a/archinstall/lib/models/pydantic.py
+++ /dev/null
@@ -1,134 +0,0 @@
-from typing import Optional, List
-from pydantic import BaseModel
-
-"""
-This python file is not in use.
-Pydantic is not a builtin, and we use the dataclasses.py instead!
-"""
-
-class VersionDef(BaseModel):
- version_string: str
-
- @classmethod
- def parse_version(self) -> List[str]:
- if '.' in self.version_string:
- versions = self.version_string.split('.')
- else:
- versions = [self.version_string]
-
- return versions
-
- @classmethod
- def major(self) -> str:
- return self.parse_version()[0]
-
- @classmethod
- def minor(self) -> str:
- versions = self.parse_version()
- if len(versions) >= 2:
- return versions[1]
-
- @classmethod
- def patch(self) -> str:
- versions = self.parse_version()
- if '-' in versions[-1]:
- _, patch_version = versions[-1].split('-', 1)
- return patch_version
-
- def __eq__(self, other :'VersionDef') -> bool:
- if other.major == self.major and \
- other.minor == self.minor and \
- other.patch == self.patch:
-
- return True
- return False
-
- def __lt__(self, other :'VersionDef') -> bool:
- if self.major > other.major:
- return False
- elif self.minor and other.minor and self.minor > other.minor:
- return False
- elif self.patch and other.patch and self.patch > other.patch:
- return False
-
- def __str__(self) -> str:
- return self.version_string
-
-
-class PackageSearchResult(BaseModel):
- pkgname: str
- pkgbase: str
- repo: str
- arch: str
- pkgver: str
- pkgrel: str
- epoch: int
- pkgdesc: str
- url: str
- filename: str
- compressed_size: int
- installed_size: int
- build_date: str
- last_update: str
- flag_date: Optional[str]
- maintainers: List[str]
- packager: str
- groups: List[str]
- licenses: List[str]
- conflicts: List[str]
- provides: List[str]
- replaces: List[str]
- depends: List[str]
- optdepends: List[str]
- makedepends: List[str]
- checkdepends: List[str]
-
- @property
- def pkg_version(self) -> str:
- return self.pkgver
-
- def __eq__(self, other :'VersionDef') -> bool:
- return self.pkg_version == other.pkg_version
-
- def __lt__(self, other :'VersionDef') -> bool:
- return self.pkg_version < other.pkg_version
-
-
-class PackageSearch(BaseModel):
- version: int
- limit: int
- valid: bool
- results: List[PackageSearchResult]
-
-
-class LocalPackage(BaseModel):
- name: str
- version: str
- description:str
- architecture: str
- url: str
- licenses: str
- groups: str
- depends_on: str
- optional_deps: str
- required_by: str
- optional_for: str
- conflicts_with: str
- replaces: str
- installed_size: str
- packager: str
- build_date: str
- install_date: str
- install_reason: str
- install_script: str
- validated_by: str
-
- @property
- def pkg_version(self) -> str:
- return self.version
-
- def __eq__(self, other :'VersionDef') -> bool:
- return self.pkg_version == other.pkg_version
-
- def __lt__(self, other :'VersionDef') -> bool:
- return self.pkg_version < other.pkg_version \ No newline at end of file
diff --git a/archinstall/lib/models/subvolume.py b/archinstall/lib/models/subvolume.py
deleted file mode 100644
index 34a09227..00000000
--- a/archinstall/lib/models/subvolume.py
+++ /dev/null
@@ -1,68 +0,0 @@
-from dataclasses import dataclass
-from typing import List, Any, Dict
-
-
-@dataclass
-class Subvolume:
- name: str
- mountpoint: str
- compress: bool = False
- nodatacow: bool = False
-
- def display(self) -> str:
- options_str = ','.join(self.options)
- return f'{_("Subvolume")}: {self.name:15} {_("Mountpoint")}: {self.mountpoint:20} {_("Options")}: {options_str}'
-
- @property
- def options(self) -> List[str]:
- options = [
- 'compress' if self.compress else '',
- 'nodatacow' if self.nodatacow else ''
- ]
- return [o for o in options if len(o)]
-
- def json(self) -> Dict[str, Any]:
- return {
- 'name': self.name,
- 'mountpoint': self.mountpoint,
- 'compress': self.compress,
- 'nodatacow': self.nodatacow
- }
-
- @classmethod
- def _parse(cls, config_subvolumes: List[Dict[str, Any]]) -> List['Subvolume']:
- subvolumes = []
- for entry in config_subvolumes:
- if not entry.get('name', None) or not entry.get('mountpoint', None):
- continue
-
- subvolumes.append(
- Subvolume(
- entry['name'],
- entry['mountpoint'],
- entry.get('compress', False),
- entry.get('nodatacow', False)
- )
- )
-
- return subvolumes
-
- @classmethod
- def _parse_backwards_compatible(cls, config_subvolumes) -> List['Subvolume']:
- subvolumes = []
- for name, mountpoint in config_subvolumes.items():
- if not name or not mountpoint:
- continue
-
- subvolumes.append(Subvolume(name, mountpoint))
-
- return subvolumes
-
- @classmethod
- def parse_arguments(cls, config_subvolumes: Any) -> List['Subvolume']:
- if isinstance(config_subvolumes, list):
- return cls._parse(config_subvolumes)
- elif isinstance(config_subvolumes, dict):
- return cls._parse_backwards_compatible(config_subvolumes)
-
- raise ValueError('Unknown disk layout btrfs subvolume format')
diff --git a/archinstall/lib/models/users.py b/archinstall/lib/models/users.py
index a8feb9ef..9ed70eef 100644
--- a/archinstall/lib/models/users.py
+++ b/archinstall/lib/models/users.py
@@ -1,12 +1,95 @@
from dataclasses import dataclass
from typing import Dict, List, Union, Any, TYPE_CHECKING
-
-from .password_strength import PasswordStrength
+from enum import Enum
if TYPE_CHECKING:
_: Any
+class PasswordStrength(Enum):
+ VERY_WEAK = 'very weak'
+ WEAK = 'weak'
+ MODERATE = 'moderate'
+ STRONG = 'strong'
+
+ @property
+ def value(self):
+ match self:
+ case PasswordStrength.VERY_WEAK: return str(_('very weak'))
+ case PasswordStrength.WEAK: return str(_('weak'))
+ case PasswordStrength.MODERATE: return str(_('moderate'))
+ case PasswordStrength.STRONG: return str(_('strong'))
+
+ def color(self):
+ match self:
+ case PasswordStrength.VERY_WEAK: return 'red'
+ case PasswordStrength.WEAK: return 'red'
+ case PasswordStrength.MODERATE: return 'yellow'
+ case PasswordStrength.STRONG: return 'green'
+
+ @classmethod
+ def strength(cls, password: str) -> 'PasswordStrength':
+ digit = any(character.isdigit() for character in password)
+ upper = any(character.isupper() for character in password)
+ lower = any(character.islower() for character in password)
+ symbol = any(not character.isalnum() for character in password)
+ return cls._check_password_strength(digit, upper, lower, symbol, len(password))
+
+ @classmethod
+ def _check_password_strength(
+ cls,
+ digit: bool,
+ upper: bool,
+ lower: bool,
+ symbol: bool,
+ length: int
+ ) -> 'PasswordStrength':
+ # suggested evaluation
+ # https://github.com/archlinux/archinstall/issues/1304#issuecomment-1146768163
+ if digit and upper and lower and symbol:
+ match length:
+ case num if 13 <= num:
+ return PasswordStrength.STRONG
+ case num if 11 <= num <= 12:
+ return PasswordStrength.MODERATE
+ case num if 7 <= num <= 10:
+ return PasswordStrength.WEAK
+ case num if num <= 6:
+ return PasswordStrength.VERY_WEAK
+ elif digit and upper and lower:
+ match length:
+ case num if 14 <= num:
+ return PasswordStrength.STRONG
+ case num if 11 <= num <= 13:
+ return PasswordStrength.MODERATE
+ case num if 7 <= num <= 10:
+ return PasswordStrength.WEAK
+ case num if num <= 6:
+ return PasswordStrength.VERY_WEAK
+ elif upper and lower:
+ match length:
+ case num if 15 <= num:
+ return PasswordStrength.STRONG
+ case num if 12 <= num <= 14:
+ return PasswordStrength.MODERATE
+ case num if 7 <= num <= 11:
+ return PasswordStrength.WEAK
+ case num if num <= 6:
+ return PasswordStrength.VERY_WEAK
+ elif lower or upper:
+ match length:
+ case num if 18 <= num:
+ return PasswordStrength.STRONG
+ case num if 14 <= num <= 17:
+ return PasswordStrength.MODERATE
+ case num if 9 <= num <= 13:
+ return PasswordStrength.WEAK
+ case num if num <= 8:
+ return PasswordStrength.VERY_WEAK
+
+ return PasswordStrength.VERY_WEAK
+
+
@dataclass
class User:
username: str
@@ -26,13 +109,6 @@ class User:
'sudo': self.sudo
}
- def display(self) -> str:
- password = '*' * (len(self.password) if self.password else 0)
- if password:
- strength = PasswordStrength.strength(self.password)
- password += f' ({strength.value})'
- return f'{_("Username")}: {self.username:16} {_("Password")}: {password:20} sudo: {str(self.sudo)}'
-
@classmethod
def _parse(cls, config_users: List[Dict[str, Any]]) -> List['User']:
users = []
diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py
index 96e8f3a1..fb26bd3d 100644
--- a/archinstall/lib/networking.py
+++ b/archinstall/lib/networking.py
@@ -1,21 +1,22 @@
-import logging
import os
import socket
+import ssl
import struct
-from typing import Union, Dict, Any, List
+from typing import Union, Dict, Any, List, Optional
+from urllib.error import URLError
+from urllib.parse import urlencode
+from urllib.request import urlopen
-from .exceptions import HardwareIncompatibilityError, SysCallError
-from .general import SysCommand
-from .output import log
-from .pacman import run_pacman
-from .storage import storage
+from .exceptions import SysCallError
+from .output import error, info
+from .pacman import Pacman
def get_hw_addr(ifname :str) -> str:
import fcntl
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
- return ':'.join('%02x' % b for b in info[18:24])
+ ret = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
+ return ':'.join('%02x' % b for b in ret[18:24])
def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]:
@@ -31,26 +32,14 @@ def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]:
return interfaces
-def check_mirror_reachable() -> bool:
- log("Testing connectivity to the Arch Linux mirrors ...", level=logging.INFO)
- try:
- if run_pacman("-Sy").exit_code == 0:
- return True
- elif os.geteuid() != 0:
- log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red")
- except SysCallError as err:
- log(err, level=logging.DEBUG)
-
- return False
-
-
def update_keyring() -> bool:
- log("Updating archlinux-keyring ...", level=logging.INFO)
- if run_pacman("-Sy --noconfirm archlinux-keyring").exit_code == 0:
+ info("Updating archlinux-keyring ...")
+ try:
+ Pacman.run("-Sy --noconfirm archlinux-keyring")
return True
-
- elif os.geteuid() != 0:
- log("update_keyring() uses 'pacman -Sy archlinux-keyring' which requires root.", level=logging.ERROR, fg="red")
+ except SysCallError:
+ if os.geteuid() != 0:
+ error("update_keyring() uses 'pacman -Sy archlinux-keyring' which requires root.")
return False
@@ -75,35 +64,20 @@ def enrich_iface_types(interfaces: Union[Dict[str, Any], List[str]]) -> Dict[str
return result
-def get_interface_from_mac(mac :str) -> str:
- return list_interfaces().get(mac.lower(), None)
-
+def fetch_data_from_url(url: str, params: Optional[Dict] = None) -> str:
+ ssl_context = ssl.create_default_context()
+ ssl_context.check_hostname = False
+ ssl_context.verify_mode = ssl.CERT_NONE
-def wireless_scan(interface :str) -> None:
- interfaces = enrich_iface_types(list_interfaces().values())
- if interfaces[interface] != 'WIRELESS':
- raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}")
+ if params is not None:
+ encoded = urlencode(params)
+ full_url = f'{url}?{encoded}'
+ else:
+ full_url = url
- if not (output := SysCommand(f"iwctl station {interface} scan")).exit_code == 0:
- raise SystemError(f"Could not scan for wireless networks: {output}")
-
- if '_WIFI' not in storage:
- storage['_WIFI'] = {}
- if interface not in storage['_WIFI']:
- storage['_WIFI'][interface] = {}
-
- storage['_WIFI'][interface]['scanning'] = True
-
-
-# TODO: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25
-def get_wireless_networks(interface :str) -> None:
- # TODO: Make this oneliner pritter to check if the interface is scanning or not.
- # TODO: Rename this to list_wireless_networks() as it doesn't return anything
- if '_WIFI' not in storage or interface not in storage['_WIFI'] or storage['_WIFI'][interface].get('scanning', False) is False:
- import time
-
- wireless_scan(interface)
- time.sleep(5)
-
- for line in SysCommand(f"iwctl station {interface} get-networks"):
- print(line)
+ try:
+ response = urlopen(full_url, context=ssl_context)
+ data = response.read().decode('UTF-8')
+ return data
+ except URLError:
+ raise ValueError(f'Unable to fetch data from url: {url}')
diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py
index 709a7382..62a1ba27 100644
--- a/archinstall/lib/output.py
+++ b/archinstall/lib/output.py
@@ -1,19 +1,28 @@
import logging
import os
import sys
+import unicodedata
+from enum import Enum
+
from pathlib import Path
-from typing import Dict, Union, List, Any, Callable
+from typing import Dict, Union, List, Any, Callable, Optional
+from dataclasses import asdict, is_dataclass
from .storage import storage
-from dataclasses import asdict, is_dataclass
class FormattedOutput:
@classmethod
- def values(cls, o: Any, class_formatter: str = None, filter_list: List[str] = None) -> Dict[str, Any]:
- """ the original values returned a dataclass as dict thru the call to some specific methods
- this version allows thru the parameter class_formatter to call a dynamicly selected formatting method.
+ def _get_values(
+ cls,
+ o: Any,
+ class_formatter: Optional[Union[str, Callable]] = None,
+ filter_list: List[str] = []
+ ) -> Dict[str, Any]:
+ """
+ the original values returned a dataclass as dict thru the call to some specific methods
+ this version allows thru the parameter class_formatter to call a dynamically selected formatting method.
Can transmit a filter list to the class_formatter,
"""
if class_formatter:
@@ -25,9 +34,10 @@ class FormattedOutput:
elif hasattr(o, class_formatter) and callable(getattr(o, class_formatter)):
func = getattr(o, class_formatter)
return func(filter_list)
- # kept as to make it backward compatible
- elif hasattr(o, 'as_json'):
- return o.as_json()
+
+ raise ValueError('Unsupported formatting call')
+ elif hasattr(o, 'table_data'):
+ return o.table_data()
elif hasattr(o, 'json'):
return o.json()
elif is_dataclass(o):
@@ -36,7 +46,13 @@ class FormattedOutput:
return o.__dict__
@classmethod
- def as_table(cls, obj: List[Any], class_formatter: Union[str, Callable] = None, filter_list: List[str] = None) -> str:
+ def as_table(
+ cls,
+ obj: List[Any],
+ class_formatter: Optional[Union[str, Callable]] = None,
+ filter_list: List[str] = [],
+ capitalize: bool = False
+ ) -> str:
""" variant of as_table (subtly different code) which has two additional parameters
filter which is a list of fields which will be shon
class_formatter a special method to format the outgoing data
@@ -45,7 +61,8 @@ class FormattedOutput:
is for compatibility with a print statement
As_table_filter can be a drop in replacement for as_table
"""
- raw_data = [cls.values(o, class_formatter, filter_list) for o in obj]
+ raw_data = [cls._get_values(o, class_formatter, filter_list) for o in obj]
+
# determine the maximum column size
column_width: Dict[str, int] = {}
for o in raw_data:
@@ -55,14 +72,20 @@ class FormattedOutput:
column_width[k] = max([column_width[k], len(str(v)), len(k)])
if not filter_list:
- filter_list = (column_width.keys())
+ filter_list = list(column_width.keys())
+
# create the header lines
output = ''
key_list = []
for key in filter_list:
width = column_width[key]
- key = key.replace('!', '')
- key_list.append(key.ljust(width))
+ key = key.replace('!', '').replace('_', ' ')
+
+ if capitalize:
+ key = key.capitalize()
+
+ key_list.append(unicode_ljust(key, width))
+
output += ' | '.join(key_list) + '\n'
output += '-' * len(output) + '\n'
@@ -72,20 +95,40 @@ class FormattedOutput:
for key in filter_list:
width = column_width.get(key, len(key))
value = record.get(key, '')
+
if '!' in key:
value = '*' * width
- if isinstance(value,(int, float)) or (isinstance(value, str) and value.isnumeric()):
- obj_data.append(str(value).rjust(width))
+
+ if isinstance(value, (int, float)) or (isinstance(value, str) and value.isnumeric()):
+ obj_data.append(unicode_rjust(str(value), width))
else:
- obj_data.append(str(value).ljust(width))
+ obj_data.append(unicode_ljust(str(value), width))
+
output += ' | '.join(obj_data) + '\n'
return output
+ @classmethod
+ def as_columns(cls, entries: List[str], cols: int) -> str:
+ """
+ Will format a list into a given number of columns
+ """
+ chunks = []
+ output = ''
+
+ for i in range(0, len(entries), cols):
+ chunks.append(entries[i:i + cols])
+
+ for row in chunks:
+ out_fmt = '{: <30} ' * len(row)
+ output += out_fmt.format(*row) + '\n'
+
+ return output
+
class Journald:
@staticmethod
- def log(message :str, level :int = logging.DEBUG) -> None:
+ def log(message: str, level: int = logging.DEBUG) -> None:
try:
import systemd.journal # type: ignore
except ModuleNotFoundError:
@@ -101,16 +144,39 @@ class Journald:
log_adapter.log(level, message)
-# TODO: Replace log() for session based logging.
-class SessionLogging:
- def __init__(self):
- pass
+def _check_log_permissions():
+ filename = storage.get('LOG_FILE', None)
+ log_dir = storage.get('LOG_PATH', Path('./'))
+
+ if not filename:
+ raise ValueError('No log file name defined')
+
+ log_file = log_dir / filename
+
+ try:
+ log_dir.mkdir(exist_ok=True, parents=True)
+ log_file.touch(exist_ok=True)
+ with log_file.open('a') as fp:
+ fp.write('')
+ except PermissionError:
+ # Fallback to creating the log file in the current folder
+ fallback_dir = Path('./').absolute()
+ fallback_log_file = fallback_dir / filename
-# Found first reference here: https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python
-# And re-used this: https://github.com/django/django/blob/master/django/core/management/color.py#L12
-def supports_color() -> bool:
+ fallback_log_file.touch(exist_ok=True)
+
+ storage['LOG_PATH'] = fallback_dir
+ warn(f'Not enough permission to place log file at {log_file}, creating it in {fallback_log_file} instead')
+
+
+def _supports_color() -> bool:
"""
+ Found first reference here:
+ https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python
+ And re-used this:
+ https://github.com/django/django/blob/master/django/core/management/color.py#L12
+
Return True if the running system's terminal supports color,
and False otherwise.
"""
@@ -121,13 +187,30 @@ def supports_color() -> bool:
return supported_platform and is_a_tty
-# Heavily influenced by: https://github.com/django/django/blob/ae8338daf34fd746771e0678081999b656177bae/django/utils/termcolors.py#L13
-# Color options here: https://askubuntu.com/questions/528928/how-to-do-underline-bold-italic-strikethrough-color-background-and-size-i
-def stylize_output(text: str, *opts :str, **kwargs) -> str:
+class Font(Enum):
+ bold = '1'
+ italic = '3'
+ underscore = '4'
+ blink = '5'
+ reverse = '7'
+ conceal = '8'
+
+
+def _stylize_output(
+ text: str,
+ fg: str,
+ bg: Optional[str],
+ reset: bool,
+ font: List[Font] = [],
+) -> str:
"""
+ Heavily influenced by:
+ https://github.com/django/django/blob/ae8338daf34fd746771e0678081999b656177bae/django/utils/termcolors.py#L13
+ Color options here:
+ https://askubuntu.com/questions/528928/how-to-do-underline-bold-italic-strikethrough-color-background-and-size-i
+
Adds styling to a text given a set of color arguments.
"""
- opt_dict = {'bold': '1', 'italic': '3', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'}
colors = {
'black' : '0',
'red' : '1',
@@ -145,65 +228,132 @@ def stylize_output(text: str, *opts :str, **kwargs) -> str:
'darkgray' : '8;5;240',
'lightgray' : '8;5;256'
}
+
foreground = {key: f'3{colors[key]}' for key in colors}
background = {key: f'4{colors[key]}' for key in colors}
- reset = '0'
-
code_list = []
- if text == '' and len(opts) == 1 and opts[0] == 'reset':
- return '\x1b[%sm' % reset
- for k, v in kwargs.items():
- if k == 'fg':
- code_list.append(foreground[str(v)])
- elif k == 'bg':
- code_list.append(background[str(v)])
+ if text == '' and reset:
+ return '\x1b[%sm' % '0'
- for o in opts:
- if o in opt_dict:
- code_list.append(opt_dict[o])
+ code_list.append(foreground[str(fg)])
- if 'noreset' not in opts:
- text = '%s\x1b[%sm' % (text or '', reset)
+ if bg:
+ code_list.append(background[str(bg)])
- return '%s%s' % (('\x1b[%sm' % ';'.join(code_list)), text or '')
+ for o in font:
+ code_list.append(o.value)
+ ansi = ';'.join(code_list)
-def log(*args :str, **kwargs :Union[str, int, Dict[str, Union[str, int]]]) -> None:
- string = orig_string = ' '.join([str(x) for x in args])
+ return f'\033[{ansi}m{text}\033[0m'
- # Attempt to colorize the output if supported
- # Insert default colors and override with **kwargs
- if supports_color():
- kwargs = {'fg': 'white', **kwargs}
- string = stylize_output(string, **kwargs)
- # If a logfile is defined in storage,
- # we use that one to output everything
- if filename := storage.get('LOG_FILE', None):
- absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
+def info(
+ *msgs: str,
+ level: int = logging.INFO,
+ fg: str = 'white',
+ bg: Optional[str] = None,
+ reset: bool = False,
+ font: List[Font] = []
+):
+ log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font)
- try:
- Path(absolute_logfile).parents[0].mkdir(exist_ok=True, parents=True)
- with open(absolute_logfile, 'a') as log_file:
- log_file.write("")
- except PermissionError:
- # Fallback to creating the log file in the current folder
- err_string = f"Not enough permission to place log file at {absolute_logfile}, creating it in {Path('./').absolute() / filename} instead."
- absolute_logfile = Path('./').absolute() / filename
- absolute_logfile.parents[0].mkdir(exist_ok=True)
- absolute_logfile = str(absolute_logfile)
- storage['LOG_PATH'] = './'
- log(err_string, fg="red")
-
- with open(absolute_logfile, 'a') as log_file:
- log_file.write(f"{orig_string}\n")
-
- Journald.log(string, level=int(str(kwargs.get('level', logging.INFO))))
-
- # Finally, print the log unless we skipped it based on level.
- # We use sys.stdout.write()+flush() instead of print() to try and
- # fix issue #94
- if kwargs.get('level', logging.INFO) != logging.DEBUG or storage['arguments'].get('verbose', False):
- sys.stdout.write(f"{string}\n")
- sys.stdout.flush()
+
+def debug(
+ *msgs: str,
+ level: int = logging.DEBUG,
+ fg: str = 'white',
+ bg: Optional[str] = None,
+ reset: bool = False,
+ font: List[Font] = []
+):
+ log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font)
+
+
+def error(
+ *msgs: str,
+ level: int = logging.ERROR,
+ fg: str = 'red',
+ bg: Optional[str] = None,
+ reset: bool = False,
+ font: List[Font] = []
+):
+ log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font)
+
+
+def warn(
+ *msgs: str,
+ level: int = logging.WARN,
+ fg: str = 'yellow',
+ bg: Optional[str] = None,
+ reset: bool = False,
+ font: List[Font] = []
+):
+ log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font)
+
+
+def log(
+ *msgs: str,
+ level: int = logging.INFO,
+ fg: str = 'white',
+ bg: Optional[str] = None,
+ reset: bool = False,
+ font: List[Font] = []
+):
+ # leave this check here as we need to setup the logging
+ # right from the beginning when the modules are loaded
+ _check_log_permissions()
+
+ text = orig_string = ' '.join([str(x) for x in msgs])
+
+ # Attempt to colorize the output if supported
+ # Insert default colors and override with **kwargs
+ if _supports_color():
+ text = _stylize_output(text, fg, bg, reset, font)
+
+ log_file: Path = storage['LOG_PATH'] / storage['LOG_FILE']
+
+ with log_file.open('a') as fp:
+ fp.write(f"{orig_string}\n")
+
+ Journald.log(text, level=level)
+
+ from .menu import Menu
+ if not Menu.is_menu_active():
+ # Finally, print the log unless we skipped it based on level.
+ # We use sys.stdout.write()+flush() instead of print() to try and
+ # fix issue #94
+ if level != logging.DEBUG or storage.get('arguments', {}).get('verbose', False):
+ sys.stdout.write(f"{text}\n")
+ sys.stdout.flush()
+
+def _count_wchars(string: str) -> int:
+ "Count the total number of wide characters contained in a string"
+ return sum(unicodedata.east_asian_width(c) in 'FW' for c in string)
+
+def unicode_ljust(string: str, width: int, fillbyte: str = ' ') -> str:
+ """Return a left-justified unicode string of length width.
+ >>> unicode_ljust('Hello', 15, '*')
+ 'Hello**********'
+ >>> unicode_ljust('你好', 15, '*')
+ '你好***********'
+ >>> unicode_ljust('안녕하세요', 15, '*')
+ '안녕하세요*****'
+ >>> unicode_ljust('ã“ã‚“ã«ã¡ã¯', 15, '*')
+ 'ã“ã‚“ã«ã¡ã¯*****'
+ """
+ return string.ljust(width - _count_wchars(string), fillbyte)
+
+def unicode_rjust(string: str, width: int, fillbyte: str = ' ') -> str:
+ """Return a right-justified unicode string of length width.
+ >>> unicode_rjust('Hello', 15, '*')
+ '**********Hello'
+ >>> unicode_rjust('你好', 15, '*')
+ '***********你好'
+ >>> unicode_rjust('안녕하세요', 15, '*')
+ '*****안녕하세요'
+ >>> unicode_rjust('ã“ã‚“ã«ã¡ã¯', 15, '*')
+ '*****ã“ã‚“ã«ã¡ã¯'
+ """
+ return string.rjust(width - _count_wchars(string), fillbyte)
diff --git a/archinstall/lib/packages/__init__.py b/archinstall/lib/packages/__init__.py
index e69de29b..e2aab577 100644
--- a/archinstall/lib/packages/__init__.py
+++ b/archinstall/lib/packages/__init__.py
@@ -0,0 +1,4 @@
+from .packages import (
+ group_search, package_search, find_package,
+ find_packages, validate_package_list, installed_package
+)
diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py
index 0743e83b..e495b03f 100644
--- a/archinstall/lib/packages/packages.py
+++ b/archinstall/lib/packages/packages.py
@@ -7,8 +7,8 @@ from urllib.parse import urlencode
from urllib.request import urlopen
from ..exceptions import PackageError, SysCallError
-from ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage
-from ..pacman import run_pacman
+from ..models.gen import PackageSearch, PackageSearchResult, LocalPackage
+from ..pacman import Pacman
BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/'
# BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'
@@ -37,7 +37,7 @@ def group_search(name :str) -> List[PackageSearchResult]:
raise err
# Just to be sure some code didn't slip through the exception
- data = response.read().decode('UTF-8')
+ data = response.read().decode('utf-8')
return [PackageSearchResult(**package) for package in json.loads(data)['results']]
@@ -55,8 +55,8 @@ def package_search(package :str) -> PackageSearch:
raise PackageError(f"Could not locate package: [{response.code}] {response}")
data = response.read().decode('UTF-8')
-
- return PackageSearch(**json.loads(data))
+ json_data = json.loads(data)
+ return PackageSearch.from_json(json_data)
def find_package(package :str) -> List[PackageSearchResult]:
@@ -106,11 +106,11 @@ def validate_package_list(packages :list) -> Tuple[list, list]:
def installed_package(package :str) -> LocalPackage:
package_info = {}
try:
- for line in run_pacman(f"-Q --info {package}"):
+ for line in Pacman.run(f"-Q --info {package}"):
if b':' in line:
key, value = line.decode().split(':', 1)
package_info[key.strip().lower().replace(' ', '_')] = value.strip()
except SysCallError:
pass
- return LocalPackage({field.name: package_info.get(field.name) for field in dataclasses.fields(LocalPackage)})
+ return LocalPackage({field.name: package_info.get(field.name) for field in dataclasses.fields(LocalPackage)}) # type: ignore
diff --git a/archinstall/lib/pacman.py b/archinstall/lib/pacman.py
deleted file mode 100644
index 9c427aff..00000000
--- a/archinstall/lib/pacman.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import logging
-import pathlib
-import time
-
-from .general import SysCommand
-from .output import log
-
-
-def run_pacman(args :str, default_cmd :str = 'pacman') -> SysCommand:
- """
- A centralized function to call `pacman` from.
- It also protects us from colliding with other running pacman sessions (if used locally).
- The grace period is set to 10 minutes before exiting hard if another pacman instance is running.
- """
- pacman_db_lock = pathlib.Path('/var/lib/pacman/db.lck')
-
- if pacman_db_lock.exists():
- log(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.'), level=logging.WARNING, fg="red")
-
- started = time.time()
- while pacman_db_lock.exists():
- time.sleep(0.25)
-
- if time.time() - started > (60 * 10):
- log(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.'), level=logging.WARNING, fg="red")
- exit(1)
-
- return SysCommand(f'{default_cmd} {args}')
diff --git a/archinstall/lib/pacman/__init__.py b/archinstall/lib/pacman/__init__.py
new file mode 100644
index 00000000..6478f0cc
--- /dev/null
+++ b/archinstall/lib/pacman/__init__.py
@@ -0,0 +1,88 @@
+from pathlib import Path
+import time
+import re
+from typing import TYPE_CHECKING, Any, List, Callable, Union
+from shutil import copy2
+
+from ..general import SysCommand
+from ..output import warn, error, info
+from .repo import Repo
+from .config import Config
+from ..exceptions import RequirementError
+from ..plugins import plugins
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class Pacman:
+
+ def __init__(self, target: Path, silent: bool = False):
+ self.synced = False
+ self.silent = silent
+ self.target = target
+
+ @staticmethod
+ def run(args :str, default_cmd :str = 'pacman') -> SysCommand:
+ """
+ A centralized function to call `pacman` from.
+ It also protects us from colliding with other running pacman sessions (if used locally).
+ The grace period is set to 10 minutes before exiting hard if another pacman instance is running.
+ """
+ pacman_db_lock = Path('/var/lib/pacman/db.lck')
+
+ if pacman_db_lock.exists():
+ warn(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.'))
+
+ started = time.time()
+ while pacman_db_lock.exists():
+ time.sleep(0.25)
+
+ if time.time() - started > (60 * 10):
+ error(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.'))
+ exit(1)
+
+ return SysCommand(f'{default_cmd} {args}')
+
+ def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs):
+ while True:
+ try:
+ func(*args, **kwargs)
+ break
+ except Exception as err:
+ error(f'{error_message}: {err}')
+ if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y':
+ continue
+ raise RequirementError(f'{bail_message}: {err}')
+
+ def sync(self):
+ if self.synced:
+ return
+ self.ask(
+ 'Could not sync a new package database',
+ 'Could not sync mirrors',
+ self.run,
+ '-Syy',
+ default_cmd='/usr/bin/pacman'
+ )
+ self.synced = True
+
+ def strap(self, packages: Union[str, List[str]]):
+ self.sync()
+ if isinstance(packages, str):
+ packages = [packages]
+
+ for plugin in plugins.values():
+ if hasattr(plugin, 'on_pacstrap'):
+ if (result := plugin.on_pacstrap(packages)):
+ packages = result
+
+ info(f'Installing packages: {packages}')
+
+ self.ask(
+ 'Could not strap in packages',
+ 'Pacstrap failed. See /var/log/archinstall/install.log or above message for error details',
+ SysCommand,
+ f'/usr/bin/pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm',
+ peek_output=True
+ )
diff --git a/archinstall/lib/pacman/config.py b/archinstall/lib/pacman/config.py
new file mode 100644
index 00000000..6686f4a9
--- /dev/null
+++ b/archinstall/lib/pacman/config.py
@@ -0,0 +1,44 @@
+import re
+from pathlib import Path
+from shutil import copy2
+from typing import List
+
+from .repo import Repo
+
+
+class Config:
+ def __init__(self, target: Path):
+ self.path = Path("/etc") / "pacman.conf"
+ self.chroot_path = target / "etc" / "pacman.conf"
+ self.repos: List[Repo] = []
+
+ def enable(self, repo: Repo):
+ self.repos.append(repo)
+
+ def apply(self):
+ if not self.repos:
+ return
+
+ if Repo.Testing in self.repos:
+ if Repo.Multilib in self.repos:
+ repos_pattern = f'({Repo.Multilib.value}|.+-{Repo.Testing.value})'
+ else:
+ repos_pattern = f'(?!{Repo.Multilib.value}).+-{Repo.Testing.value}'
+ else:
+ repos_pattern = Repo.Multilib.value
+
+ pattern = re.compile(rf"^#\s*\[{repos_pattern}\]$")
+
+ lines = iter(self.path.read_text().splitlines(keepends=True))
+ with open(self.path, 'w') as f:
+ for line in lines:
+ if pattern.match(line):
+ # Uncomment this line and the next.
+ f.write(line.lstrip('#'))
+ f.write(next(lines).lstrip('#'))
+ else:
+ f.write(line)
+
+ def persist(self):
+ if self.repos:
+ copy2(self.path, self.chroot_path)
diff --git a/archinstall/lib/pacman/repo.py b/archinstall/lib/pacman/repo.py
new file mode 100644
index 00000000..7a461431
--- /dev/null
+++ b/archinstall/lib/pacman/repo.py
@@ -0,0 +1,5 @@
+from enum import Enum
+
+class Repo(Enum):
+ Multilib = "multilib"
+ Testing = "testing"
diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py
index 0ff63610..4ccb0666 100644
--- a/archinstall/lib/plugins.py
+++ b/archinstall/lib/plugins.py
@@ -1,105 +1,120 @@
import hashlib
import importlib
-import logging
import os
import sys
-import pathlib
import urllib.parse
import urllib.request
from importlib import metadata
+from pathlib import Path
from typing import Optional, List
-from types import ModuleType
-from .output import log
+from .output import error, info, warn
from .storage import storage
plugins = {}
+
# 1: List archinstall.plugin definitions
# 2: Load the plugin entrypoint
# 3: Initiate the plugin and store it as .name in plugins
for plugin_definition in metadata.entry_points().select(group='archinstall.plugin'):
plugin_entrypoint = plugin_definition.load()
+
try:
plugins[plugin_definition.name] = plugin_entrypoint()
except Exception as err:
- log(err, level=logging.ERROR)
- log(f"The above error was detected when loading the plugin: {plugin_definition}", fg="red", level=logging.ERROR)
+ error(
+ f'Error: {err}',
+ f"The above error was detected when loading the plugin: {plugin_definition}"
+ )
+
+def _localize_path(path: Path) -> Path:
+ """
+ Support structures for load_plugin()
+ """
+ url = urllib.parse.urlparse(str(path))
-# The following functions and core are support structures for load_plugin()
-def localize_path(profile_path :str) -> str:
- if (url := urllib.parse.urlparse(profile_path)).scheme and url.scheme in ('https', 'http'):
- converted_path = f"/tmp/{os.path.basename(profile_path).replace('.py', '')}_{hashlib.md5(os.urandom(12)).hexdigest()}.py"
+ 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 profile_path
+ return path
-def import_via_path(path :str, namespace :Optional[str] = None) -> ModuleType:
+def _import_via_path(path: Path, namespace: Optional[str] = None) -> Optional[str]:
if not namespace:
namespace = os.path.basename(path)
if namespace == '__init__.py':
- path = pathlib.PurePath(path)
namespace = path.parent.name
try:
spec = importlib.util.spec_from_file_location(namespace, path)
- imported = importlib.util.module_from_spec(spec)
- sys.modules[namespace] = imported
- spec.loader.exec_module(sys.modules[namespace])
+ if spec and spec.loader:
+ imported = importlib.util.module_from_spec(spec)
+ sys.modules[namespace] = imported
+ spec.loader.exec_module(sys.modules[namespace])
return namespace
except Exception as err:
- log(err, level=logging.ERROR)
- log(f"The above error was detected when loading the plugin: {path}", fg="red", level=logging.ERROR)
+ error(
+ f'Error: {err}',
+ f"The above error was detected when loading the plugin: {path}"
+ )
try:
- del(sys.modules[namespace]) # noqa: E275
- except:
+ del sys.modules[namespace]
+ except Exception:
pass
-def find_nth(haystack :List[str], needle :str, n :int) -> int:
- start = haystack.find(needle)
- while start >= 0 and n > 1:
- start = haystack.find(needle, start + len(needle))
- n -= 1
- return start
+ return namespace
+
+
+def _find_nth(haystack: List[str], needle: str, n: int) -> Optional[int]:
+ indices = [idx for idx, elem in enumerate(haystack) if elem == needle]
+ if n <= len(indices):
+ return indices[n - 1]
+ return None
+
-def load_plugin(path :str) -> ModuleType:
- parsed_url = urllib.parse.urlparse(path)
- log(f"Loading plugin {parsed_url}.", fg="gray", level=logging.INFO)
+def load_plugin(path: Path):
+ namespace: Optional[str] = None
+ parsed_url = urllib.parse.urlparse(str(path))
+ info(f"Loading plugin from url {parsed_url}")
# 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)
+ namespace = _import_via_path(path)
elif parsed_url.scheme in ('https', 'http'):
- namespace = import_via_path(localize_path(path))
+ localized = _localize_path(path)
+ namespace = _import_via_path(localized)
- if namespace in sys.modules:
+ if namespace and namespace in sys.modules:
# Version dependency via __archinstall__version__ variable (if present) in the plugin
# Any errors in version inconsistency will be handled through normal error handling if not defined.
if hasattr(sys.modules[namespace], '__archinstall__version__'):
- archinstall_major_and_minor_version = float(storage['__version__'][:find_nth(storage['__version__'], '.', 2)])
+ archinstall_major_and_minor_version = float(storage['__version__'][:_find_nth(storage['__version__'], '.', 2)])
if sys.modules[namespace].__archinstall__version__ < archinstall_major_and_minor_version:
- log(f"Plugin {sys.modules[namespace]} does not support the current Archinstall version.", fg="red", level=logging.ERROR)
+ error(f"Plugin {sys.modules[namespace]} does not support the current Archinstall version.")
# Locate the plugin entry-point called Plugin()
# This in accordance with the entry_points() from setup.cfg above
if hasattr(sys.modules[namespace], 'Plugin'):
try:
plugins[namespace] = sys.modules[namespace].Plugin()
- log(f"Plugin {plugins[namespace]} has been loaded.", fg="gray", level=logging.INFO)
+ info(f"Plugin {plugins[namespace]} has been loaded.")
except Exception as err:
- log(err, level=logging.ERROR)
- log(f"The above error was detected when initiating the plugin: {path}", fg="red", level=logging.ERROR)
+ error(
+ f'Error: {err}',
+ f"The above error was detected when initiating the plugin: {path}"
+ )
else:
- log(f"Plugin '{path}' is missing a valid entry-point or is corrupt.", fg="yellow", level=logging.WARNING)
+ warn(f"Plugin '{path}' is missing a valid entry-point or is corrupt.")
diff --git a/archinstall/lib/profile/__init__.py b/archinstall/lib/profile/__init__.py
new file mode 100644
index 00000000..6e74b0d8
--- /dev/null
+++ b/archinstall/lib/profile/__init__.py
@@ -0,0 +1,3 @@
+from .profile_menu import ProfileMenu, select_greeter, select_profile
+from .profiles_handler import profile_handler
+from .profile_model import ProfileConfiguration
diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py
new file mode 100644
index 00000000..aba75a88
--- /dev/null
+++ b/archinstall/lib/profile/profile_menu.py
@@ -0,0 +1,218 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Optional, Dict
+
+from archinstall.default_profiles.profile import Profile, GreeterType
+from .profile_model import ProfileConfiguration
+from ..menu import Menu, MenuSelectionType, AbstractSubMenu, Selector
+from ..interactions.system_conf import select_driver
+from ..hardware import GfxDriver
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class ProfileMenu(AbstractSubMenu):
+ def __init__(
+ self,
+ data_store: Dict[str, Any],
+ preset: Optional[ProfileConfiguration] = None
+ ):
+ if preset:
+ self._preset = preset
+ else:
+ self._preset = ProfileConfiguration()
+
+ super().__init__(data_store=data_store)
+
+ def setup_selection_menu_options(self):
+ self._menu_options['profile'] = Selector(
+ _('Type'),
+ lambda x: self._select_profile(x),
+ display_func=lambda x: x.name if x else None,
+ preview_func=self._preview_profile,
+ default=self._preset.profile,
+ enabled=True
+ )
+
+ self._menu_options['gfx_driver'] = Selector(
+ _('Graphics driver'),
+ lambda preset: self._select_gfx_driver(preset),
+ display_func=lambda x: x.value if x else None,
+ dependencies=['profile'],
+ preview_func=self._preview_gfx,
+ default=self._preset.gfx_driver if self._preset.profile and self._preset.profile.is_graphic_driver_supported() else None,
+ enabled=self._preset.profile.is_graphic_driver_supported() if self._preset.profile else False
+ )
+
+ self._menu_options['greeter'] = Selector(
+ _('Greeter'),
+ lambda preset: select_greeter(self._menu_options['profile'].current_selection, preset),
+ display_func=lambda x: x.value if x else None,
+ dependencies=['profile'],
+ default=self._preset.greeter if self._preset.profile and self._preset.profile.is_greeter_supported() else None,
+ enabled=self._preset.profile.is_greeter_supported() if self._preset.profile else False
+ )
+
+ def run(self, allow_reset: bool = True) -> Optional[ProfileConfiguration]:
+ super().run(allow_reset=allow_reset)
+
+ if self._data_store.get('profile', None):
+ return ProfileConfiguration(
+ self._menu_options['profile'].current_selection,
+ self._menu_options['gfx_driver'].current_selection,
+ self._menu_options['greeter'].current_selection
+ )
+
+ return None
+
+ def _select_profile(self, preset: Optional[Profile]) -> Optional[Profile]:
+ profile = select_profile(preset)
+
+ if profile is not None:
+ if not profile.is_graphic_driver_supported():
+ self._menu_options['gfx_driver'].set_enabled(False)
+ self._menu_options['gfx_driver'].set_current_selection(None)
+ else:
+ self._menu_options['gfx_driver'].set_enabled(True)
+ self._menu_options['gfx_driver'].set_current_selection(GfxDriver.AllOpenSource)
+
+ if not profile.is_greeter_supported():
+ self._menu_options['greeter'].set_enabled(False)
+ self._menu_options['greeter'].set_current_selection(None)
+ else:
+ self._menu_options['greeter'].set_enabled(True)
+ self._menu_options['greeter'].set_current_selection(profile.default_greeter_type)
+ else:
+ self._menu_options['gfx_driver'].set_current_selection(None)
+ self._menu_options['greeter'].set_current_selection(None)
+
+ return profile
+
+ def _select_gfx_driver(self, preset: Optional[GfxDriver] = None) -> Optional[GfxDriver]:
+ driver = preset
+ profile: Optional[Profile] = self._menu_options['profile'].current_selection
+
+ if profile:
+ if profile.is_graphic_driver_supported():
+ driver = select_driver(current_value=preset)
+
+ if driver and 'Sway' in profile.current_selection_names():
+ if driver.is_nvidia():
+ prompt = str(_('The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?'))
+ choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run()
+
+ if choice.value == Menu.no():
+ return None
+
+ return driver
+
+ def _preview_gfx(self) -> Optional[str]:
+ driver: Optional[GfxDriver] = self._menu_options['gfx_driver'].current_selection
+
+ if driver:
+ return driver.packages_text()
+
+ return None
+
+ def _preview_profile(self) -> Optional[str]:
+ profile: Optional[Profile] = self._menu_options['profile'].current_selection
+ text = ''
+
+ if profile:
+ if (sub_profiles := profile.current_selection) is not None:
+ text += str(_('Selected profiles: '))
+ text += ', '.join([p.name for p in sub_profiles]) + '\n'
+
+ if packages := profile.packages_text(include_sub_packages=True):
+ text += f'{packages}'
+
+ if text:
+ return text
+
+ return None
+
+
+def select_greeter(
+ profile: Optional[Profile] = None,
+ preset: Optional[GreeterType] = None
+) -> Optional[GreeterType]:
+ if not profile or profile.is_greeter_supported():
+ title = str(_('Please chose which greeter to install'))
+ greeter_options = [greeter.value for greeter in GreeterType]
+
+ default: Optional[GreeterType] = None
+
+ if preset is not None:
+ default = preset
+ elif profile is not None:
+ default_greeter = profile.default_greeter_type
+ default = default_greeter if default_greeter else None
+
+ choice = Menu(
+ title,
+ greeter_options,
+ skip=True,
+ default_option=default.value if default else None
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Skip:
+ return default
+
+ return GreeterType(choice.single_value)
+
+ return None
+
+
+def select_profile(
+ current_profile: Optional[Profile] = None,
+ title: Optional[str] = None,
+ allow_reset: bool = True,
+ multi: bool = False
+) -> Optional[Profile]:
+ from archinstall.lib.profile.profiles_handler import profile_handler
+ top_level_profiles = profile_handler.get_top_level_profiles()
+
+ display_title = title
+ if not display_title:
+ display_title = str(_('This is a list of pre-programmed default_profiles'))
+
+ choice = profile_handler.select_profile(
+ top_level_profiles,
+ current_profile=current_profile,
+ title=display_title,
+ allow_reset=allow_reset,
+ multi=multi
+ )
+
+ match choice.type_:
+ case MenuSelectionType.Selection:
+ profile_selection: Profile = choice.single_value
+ select_result = profile_selection.do_on_select()
+
+ if not select_result:
+ return select_profile(
+ current_profile=current_profile,
+ title=title,
+ allow_reset=allow_reset,
+ multi=multi
+ )
+
+ # we're going to reset the currently selected profile(s) to avoid
+ # any stale data laying around
+ match select_result:
+ case select_result.NewSelection:
+ profile_handler.reset_top_level_profiles(exclude=[profile_selection])
+ current_profile = profile_selection
+ case select_result.ResetCurrent:
+ profile_handler.reset_top_level_profiles()
+ current_profile = None
+ case select_result.SameSelection:
+ pass
+
+ return current_profile
+ case MenuSelectionType.Reset:
+ return None
+ case MenuSelectionType.Skip:
+ return current_profile
diff --git a/archinstall/lib/profile/profile_model.py b/archinstall/lib/profile/profile_model.py
new file mode 100644
index 00000000..8c955733
--- /dev/null
+++ b/archinstall/lib/profile/profile_model.py
@@ -0,0 +1,39 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import TYPE_CHECKING, Any, Optional, Dict
+
+from ..hardware import GfxDriver
+from archinstall.default_profiles.profile import Profile, GreeterType
+
+if TYPE_CHECKING:
+ _: Any
+
+
+@dataclass
+class ProfileConfiguration:
+ profile: Optional[Profile] = None
+ gfx_driver: Optional[GfxDriver] = None
+ greeter: Optional[GreeterType] = None
+
+ def json(self) -> Dict[str, Any]:
+ from .profiles_handler import profile_handler
+ return {
+ 'profile': profile_handler.to_json(self.profile),
+ 'gfx_driver': self.gfx_driver.value if self.gfx_driver else None,
+ 'greeter': self.greeter.value if self.greeter else None
+ }
+
+ @classmethod
+ def parse_arg(cls, arg: Dict[str, Any]) -> 'ProfileConfiguration':
+ from .profiles_handler import profile_handler
+
+ profile = profile_handler.parse_profile_config(arg['profile'])
+ greeter = arg.get('greeter', None)
+ gfx_driver = arg.get('gfx_driver', None)
+
+ return ProfileConfiguration(
+ profile,
+ GfxDriver(gfx_driver) if gfx_driver else None,
+ GreeterType(greeter) if greeter else None
+ )
diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py
new file mode 100644
index 00000000..b9acb4fe
--- /dev/null
+++ b/archinstall/lib/profile/profiles_handler.py
@@ -0,0 +1,413 @@
+from __future__ import annotations
+
+import importlib.util
+import sys
+import inspect
+from collections import Counter
+from functools import cached_property
+from pathlib import Path
+from tempfile import NamedTemporaryFile
+from types import ModuleType
+from typing import List, TYPE_CHECKING, Any, Optional, Dict, Union
+
+from archinstall.default_profiles.profile import Profile, TProfile, GreeterType
+from .profile_model import ProfileConfiguration
+from ..hardware import GfxDriver
+from ..menu import MenuSelectionType, Menu, MenuSelection
+from ..networking import list_interfaces, fetch_data_from_url
+from ..output import error, debug, info
+from ..storage import storage
+
+if TYPE_CHECKING:
+ from ..installer import Installer
+ _: Any
+
+
+class ProfileHandler:
+ def __init__(self):
+ self._profiles_path: Path = storage['PROFILE']
+ self._profiles = None
+
+ # special variable to keep track of a profile url configuration
+ # it is merely used to be able to export the path again when a user
+ # wants to save the configuration
+ self._url_path = None
+
+ def to_json(self, profile: Optional[Profile]) -> Dict[str, Any]:
+ """
+ Serialize the selected profile setting to JSON
+ """
+ data: Dict[str, Any] = {}
+
+ if profile is not None:
+ data = {
+ 'main': profile.name,
+ 'details': [profile.name for profile in profile.current_selection],
+ 'custom_settings': {profile.name: profile.custom_settings for profile in profile.current_selection}
+ }
+
+ if self._url_path is not None:
+ data['path'] = self._url_path
+
+ return data
+
+ def parse_profile_config(self, profile_config: Dict[str, Any]) -> Optional[Profile]:
+ """
+ Deserialize JSON configuration for profile
+ """
+ profile: Optional[Profile] = None
+
+ # the order of these is important, we want to
+ # load all the default_profiles from url and custom
+ # so that we can then apply whatever was specified
+ # in the main/detail sections
+ if url_path := profile_config.get('path', None):
+ self._url_path = url_path
+ local_path = Path(url_path)
+
+ if local_path.is_file():
+ profiles = self._process_profile_file(local_path)
+ self.remove_custom_profiles(profiles)
+ self.add_custom_profiles(profiles)
+ else:
+ self._import_profile_from_url(url_path)
+
+ # if custom := profile_config.get('custom', None):
+ # from archinstall.default_profiles.custom import CustomTypeProfile
+ # custom_types = []
+ #
+ # for entry in custom:
+ # custom_types.append(
+ # CustomTypeProfile(
+ # entry['name'],
+ # entry['enabled'],
+ # entry.get('packages', []),
+ # entry.get('services', [])
+ # )
+ # )
+ #
+ # self.remove_custom_profiles(custom_types)
+ # self.add_custom_profiles(custom_types)
+ #
+ # # this doesn't mean it's actual going to be set as a selection
+ # # but we are simply populating the custom profile with all
+ # # possible custom definitions
+ # if custom_profile := self.get_profile_by_name('Custom'):
+ # custom_profile.set_current_selection(custom_types)
+
+ if main := profile_config.get('main', None):
+ profile = self.get_profile_by_name(main) if main else None
+
+ if not profile:
+ return None
+
+ valid_sub_profiles: List[Profile] = []
+ invalid_sub_profiles: List[str] = []
+ details: List[str] = profile_config.get('details', [])
+
+ if details:
+ for detail in filter(None, details):
+ # [2024-04-19] TODO: Backwards compatibility after naming change: https://github.com/archlinux/archinstall/pull/2421
+ # 'Kde' is deprecated, remove this block in a future version
+ if detail == 'Kde':
+ detail = 'KDE Plasma'
+
+ if sub_profile := self.get_profile_by_name(detail):
+ valid_sub_profiles.append(sub_profile)
+ else:
+ invalid_sub_profiles.append(detail)
+
+ if invalid_sub_profiles:
+ info('No profile definition found: {}'.format(', '.join(invalid_sub_profiles)))
+
+ custom_settings = profile_config.get('custom_settings', {})
+ profile.set_custom_settings(custom_settings)
+ profile.set_current_selection(valid_sub_profiles)
+
+ return profile
+
+ @property
+ def profiles(self) -> List[Profile]:
+ """
+ List of all available default_profiles
+ """
+ self._profiles = self._profiles or self._find_available_profiles()
+ return self._profiles
+
+ @cached_property
+ def _local_mac_addresses(self) -> List[str]:
+ return list(list_interfaces())
+
+ def add_custom_profiles(self, profiles: Union[TProfile, List[TProfile]]):
+ if not isinstance(profiles, list):
+ profiles = [profiles]
+
+ for profile in profiles:
+ self.profiles.append(profile)
+
+ self._verify_unique_profile_names(self.profiles)
+
+ def remove_custom_profiles(self, profiles: Union[TProfile, List[TProfile]]):
+ if not isinstance(profiles, list):
+ profiles = [profiles]
+
+ remove_names = [p.name for p in profiles]
+ self._profiles = [p for p in self.profiles if p.name not in remove_names]
+
+ def get_profile_by_name(self, name: str) -> Optional[Profile]:
+ return next(filter(lambda x: x.name == name, self.profiles), None) # type: ignore
+
+ def get_top_level_profiles(self) -> List[Profile]:
+ return list(filter(lambda x: x.is_top_level_profile(), self.profiles))
+
+ def get_server_profiles(self) -> List[Profile]:
+ return list(filter(lambda x: x.is_server_type_profile(), self.profiles))
+
+ def get_desktop_profiles(self) -> List[Profile]:
+ return list(filter(lambda x: x.is_desktop_type_profile(), self.profiles))
+
+ def get_custom_profiles(self) -> List[Profile]:
+ return list(filter(lambda x: x.is_custom_type_profile(), self.profiles))
+
+ def get_mac_addr_profiles(self) -> List[Profile]:
+ tailored = list(filter(lambda x: x.is_tailored(), self.profiles))
+ match_mac_addr_profiles = list(filter(lambda x: x.name in self._local_mac_addresses, tailored))
+ return match_mac_addr_profiles
+
+ def install_greeter(self, install_session: 'Installer', greeter: GreeterType):
+ packages = []
+ service = None
+
+ match greeter:
+ case GreeterType.LightdmSlick:
+ packages = ['lightdm', 'lightdm-slick-greeter']
+ service = ['lightdm']
+ case GreeterType.Lightdm:
+ packages = ['lightdm', 'lightdm-gtk-greeter']
+ service = ['lightdm']
+ case GreeterType.Sddm:
+ packages = ['sddm']
+ service = ['sddm']
+ case GreeterType.Gdm:
+ packages = ['gdm']
+ service = ['gdm']
+ case GreeterType.Ly:
+ packages = ['ly']
+ service = ['ly']
+
+ if packages:
+ install_session.add_additional_packages(packages)
+ if service:
+ install_session.enable_service(service)
+
+ # slick-greeter requires a config change
+ if greeter == GreeterType.LightdmSlick:
+ path = install_session.target.joinpath('etc/lightdm/lightdm.conf')
+ with open(path, 'r') as file:
+ filedata = file.read()
+
+ filedata = filedata.replace('#greeter-session=example-gtk-gnome', 'greeter-session=lightdm-slick-greeter')
+
+ with open(path, 'w') as file:
+ file.write(filedata)
+
+ def install_gfx_driver(self, install_session: 'Installer', driver: GfxDriver):
+ debug(f'Installing GFX driver: {driver.value}')
+
+ if driver in [GfxDriver.NvidiaOpenKernel, GfxDriver.NvidiaProprietary]:
+ headers = [f'{kernel}-headers' for kernel in install_session.kernels]
+ # Fixes https://github.com/archlinux/archinstall/issues/585
+ install_session.add_additional_packages(headers)
+ elif driver in [GfxDriver.AllOpenSource, GfxDriver.AmdOpenSource]:
+ # The order of these two are important if amdgpu is installed #808
+ install_session.remove_mod('amdgpu')
+ install_session.remove_mod('radeon')
+
+ install_session.append_mod('amdgpu')
+ install_session.append_mod('radeon')
+
+ driver_pkgs = driver.gfx_packages()
+ pkg_names = [p.value for p in driver_pkgs]
+ install_session.add_additional_packages(pkg_names)
+
+ def install_profile_config(self, install_session: 'Installer', profile_config: ProfileConfiguration):
+ profile = profile_config.profile
+
+ if not profile:
+ return
+
+ profile.install(install_session)
+
+ if profile_config.gfx_driver and (profile.is_xorg_type_profile() or profile.is_desktop_profile()):
+ self.install_gfx_driver(install_session, profile_config.gfx_driver)
+
+ if profile_config.greeter:
+ self.install_greeter(install_session, profile_config.greeter)
+
+ def _import_profile_from_url(self, url: str):
+ """
+ Import default_profiles from a url path
+ """
+ try:
+ data = fetch_data_from_url(url)
+ b_data = bytes(data, 'utf-8')
+
+ with NamedTemporaryFile(delete=False, suffix='.py') as fp:
+ fp.write(b_data)
+ filepath = Path(fp.name)
+
+ profiles = self._process_profile_file(filepath)
+ self.remove_custom_profiles(profiles)
+ self.add_custom_profiles(profiles)
+ except ValueError:
+ err = str(_('Unable to fetch profile from specified url: {}')).format(url)
+ error(err)
+
+ def _load_profile_class(self, module: ModuleType) -> List[Profile]:
+ """
+ Load all default_profiles defined in a module
+ """
+ profiles = []
+ for k, v in module.__dict__.items():
+ if isinstance(v, type) and v.__module__ == module.__name__:
+ bases = inspect.getmro(v)
+
+ if Profile in bases:
+ try:
+ cls_ = v()
+ if isinstance(cls_, Profile):
+ profiles.append(cls_)
+ except Exception:
+ debug(f'Cannot import {module}, it does not appear to be a Profile class')
+
+ return profiles
+
+ def _verify_unique_profile_names(self, profiles: List[Profile]):
+ """
+ All profile names have to be unique, this function will verify
+ that the provided list contains only default_profiles with unique names
+ """
+ counter = Counter([p.name for p in profiles])
+ duplicates = list(filter(lambda x: x[1] != 1, counter.items()))
+
+ if len(duplicates) > 0:
+ err = str(_('Profiles must have unique name, but profile definitions with duplicate name found: {}')).format(duplicates[0][0])
+ error(err)
+ sys.exit(1)
+
+ def _is_legacy(self, file: Path) -> bool:
+ """
+ Check if the provided profile file contains a
+ legacy profile definition
+ """
+ with open(file, 'r') as fp:
+ for line in fp.readlines():
+ if '__packages__' in line:
+ return True
+ return False
+
+ def _process_profile_file(self, file: Path) -> List[Profile]:
+ """
+ Process a file for profile definitions
+ """
+ if self._is_legacy(file):
+ info(f'Cannot import {file} because it is no longer supported, please use the new profile format')
+ return []
+
+ if not file.is_file():
+ info(f'Cannot find profile file {file}')
+ return []
+
+ name = file.name.removesuffix(file.suffix)
+ debug(f'Importing profile: {file}')
+
+ try:
+ if spec := importlib.util.spec_from_file_location(name, file):
+ imported = importlib.util.module_from_spec(spec)
+ if spec.loader is not None:
+ spec.loader.exec_module(imported)
+ return self._load_profile_class(imported)
+ except Exception as e:
+ error(f'Unable to parse file {file}: {e}')
+
+ return []
+
+ def _find_available_profiles(self) -> List[Profile]:
+ """
+ Search the profile path for profile definitions
+ """
+ profiles = []
+ for file in self._profiles_path.glob('**/*.py'):
+ # ignore the abstract default_profiles class
+ if 'profile.py' in file.name:
+ continue
+ profiles += self._process_profile_file(file)
+
+ self._verify_unique_profile_names(profiles)
+ return profiles
+
+ def reset_top_level_profiles(self, exclude: List[Profile] = []):
+ """
+ Reset all top level profile configurations, this is usually necessary
+ when a new top level profile is selected
+ """
+ excluded_profiles = [p.name for p in exclude]
+ for profile in self.get_top_level_profiles():
+ if profile.name not in excluded_profiles:
+ profile.reset()
+
+ def select_profile(
+ self,
+ selectable_profiles: List[Profile],
+ current_profile: Optional[Union[TProfile, List[TProfile]]] = None,
+ title: str = '',
+ allow_reset: bool = True,
+ multi: bool = False,
+ ) -> MenuSelection:
+ """
+ Helper function to perform a profile selection
+ """
+ options = {p.name: p for p in selectable_profiles}
+ options = dict((k, v) for k, v in sorted(options.items(), key=lambda x: x[0].upper()))
+
+ warning = str(_('Are you sure you want to reset this setting?'))
+
+ preset_value: Optional[Union[str, List[str]]] = None
+ if current_profile is not None:
+ if isinstance(current_profile, list):
+ preset_value = [p.name for p in current_profile]
+ else:
+ preset_value = current_profile.name
+
+ choice = Menu(
+ title=title,
+ preset_values=preset_value,
+ p_options=options,
+ allow_reset=allow_reset,
+ allow_reset_warning_msg=warning,
+ multi=multi,
+ sort=False,
+ preview_command=self.preview_text,
+ preview_size=0.5
+ ).run()
+
+ if choice.type_ == MenuSelectionType.Selection:
+ value = choice.value
+ if multi:
+ # this is quite dirty and should eb switched to a
+ # dedicated return type instead
+ choice.value = [options[val] for val in value] # type: ignore
+ else:
+ choice.value = options[value] # type: ignore
+
+ return choice
+
+ def preview_text(self, selection: str) -> Optional[str]:
+ """
+ Callback for preview display on profile selection
+ """
+ profile = self.get_profile_by_name(selection)
+ return profile.preview_text() if profile is not None else None
+
+
+profile_handler = ProfileHandler()
diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py
deleted file mode 100644
index a4fbe490..00000000
--- a/archinstall/lib/profiles.py
+++ /dev/null
@@ -1,340 +0,0 @@
-from __future__ import annotations
-import hashlib
-import importlib.util
-import json
-import os
-import re
-import ssl
-import sys
-import urllib.error
-import urllib.parse
-import urllib.request
-from typing import Optional, Dict, Union, TYPE_CHECKING, Any
-from types import ModuleType
-# https://stackoverflow.com/a/39757388/929999
-if TYPE_CHECKING:
- from .installer import Installer
- _: Any
-
-from .general import multisplit
-from .networking import list_interfaces
-from .storage import storage
-from .exceptions import ProfileNotFound
-
-
-def grab_url_data(path :str) -> str:
- safe_path = path[: path.find(':') + 1] + ''.join([item if item in ('/', '?', '=', '&') else urllib.parse.quote(item) for item in multisplit(path[path.find(':') + 1:], ('/', '?', '=', '&'))])
- ssl_context = ssl.create_default_context()
- ssl_context.check_hostname = False
- ssl_context.verify_mode = ssl.CERT_NONE
- response = urllib.request.urlopen(safe_path, context=ssl_context)
- return response.read() # bytes?
-
-
-def is_desktop_profile(profile :str) -> bool:
- if str(profile) == 'Profile(desktop)':
- return True
-
- desktop_profile = Profile(None, "desktop")
- with open(desktop_profile.path, 'r') as source:
- source_data = source.read()
-
- if '__name__' in source_data and '__supported__' in source_data:
- with desktop_profile.load_instructions(namespace=f"{desktop_profile.namespace}.py") as imported:
- if hasattr(imported, '__supported__'):
- desktop_profiles = imported.__supported__
- return str(profile) in [f"Profile({s})" for s in desktop_profiles]
-
- return False
-
-
-def list_profiles(
- filter_irrelevant_macs :bool = True,
- subpath :str = '',
- filter_top_level_profiles :bool = False
-) -> Dict[str, Dict[str, Union[str, bool]]]:
- # TODO: Grab from github page as well, not just local static files
-
- if filter_irrelevant_macs:
- local_macs = list_interfaces()
-
- cache = {}
- # Grab all local profiles found in PROFILE_PATH
- for PATH_ITEM in storage['PROFILE_PATH']:
- for root, folders, files in os.walk(os.path.abspath(os.path.expanduser(PATH_ITEM + subpath))):
- for file in files:
- if file == '__init__.py':
- continue
- if os.path.splitext(file)[1] == '.py':
- tailored = False
- if len(mac := re.findall('(([a-zA-z0-9]{2}[-:]){5}([a-zA-z0-9]{2}))', file)):
- if filter_irrelevant_macs and mac[0][0].lower() not in local_macs:
- continue
- tailored = True
-
- description = ''
- with open(os.path.join(root, file), 'r') as fh:
- first_line = fh.readline()
- if len(first_line) and first_line[0] == '#':
- description = first_line[1:].strip()
-
- cache[file[:-3]] = {'path': os.path.join(root, file), 'description': description, 'tailored': tailored}
- break
-
- # Grab profiles from upstream URL
- if storage['PROFILE_DB']:
- profiles_url = os.path.join(storage["UPSTREAM_URL"] + subpath, storage['PROFILE_DB'])
- try:
- profile_list = json.loads(grab_url_data(profiles_url))
- except urllib.error.HTTPError as err:
- print(_('Error: Listing profiles on URL "{}" resulted in:').format(profiles_url), err)
- return cache
- except json.decoder.JSONDecodeError as err:
- print(_('Error: Could not decode "{}" result as JSON:').format(profiles_url), err)
- return cache
-
- for profile in profile_list:
- if os.path.splitext(profile)[1] == '.py':
- tailored = False
- if len(mac := re.findall('(([a-zA-z0-9]{2}[-:]){5}([a-zA-z0-9]{2}))', profile)):
- if filter_irrelevant_macs and mac[0][0].lower() not in local_macs:
- continue
- tailored = True
-
- cache[profile[:-3]] = {'path': os.path.join(storage["UPSTREAM_URL"] + subpath, profile), 'description': profile_list[profile], 'tailored': tailored}
-
- if filter_top_level_profiles:
- for profile in list(cache.keys()):
- if Profile(None, profile).is_top_level_profile() is False:
- del cache[profile]
-
- return cache
-
-
-class Script:
- def __init__(self, profile :str, installer :Optional[Installer] = None):
- """
- :param profile: A string representing either a boundled profile, a local python file
- or a remote path (URL) to a python script-profile. Three examples:
- * profile: https://archlinux.org/some_profile.py
- * profile: desktop
- * profile: /path/to/profile.py
- """
- self.profile = profile
- self.installer = installer # TODO: Appears not to be used anymore?
- self.converted_path = None
- self.spec = None
- self.examples = {}
- self.namespace = os.path.splitext(os.path.basename(self.path))[0]
- self.original_namespace = self.namespace
-
- def __enter__(self, *args :str, **kwargs :str) -> ModuleType:
- self.execute()
- return sys.modules[self.namespace]
-
- def __exit__(self, *args :str, **kwargs :str) -> None:
- # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
- if len(args) >= 2 and args[1]:
- raise args[1]
-
- if self.original_namespace:
- self.namespace = self.original_namespace
-
- def localize_path(self, profile_path :str) -> str:
- if (url := urllib.parse.urlparse(profile_path)).scheme and url.scheme in ('https', 'http'):
- if not self.converted_path:
- self.converted_path = f"/tmp/{os.path.basename(self.profile).replace('.py', '')}_{hashlib.md5(os.urandom(12)).hexdigest()}.py"
-
- with open(self.converted_path, "w") as temp_file:
- temp_file.write(urllib.request.urlopen(url.geturl()).read().decode('utf-8'))
-
- return self.converted_path
- else:
- return profile_path
-
- @property
- def path(self) -> str:
- parsed_url = urllib.parse.urlparse(self.profile)
-
- # The Profile was not a direct match on a remote URL
- if not parsed_url.scheme:
- # Try to locate all local or known URL's
- if not self.examples:
- self.examples = list_profiles()
-
- if f"{self.profile}" in self.examples:
- return self.localize_path(self.examples[self.profile]['path'])
- # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now:
- elif f"{self.profile}.py" in self.examples:
- return self.localize_path(self.examples[f"{self.profile}.py"]['path'])
-
- # Path was not found in any known examples, check if it's an absolute path
- if os.path.isfile(self.profile):
- return self.profile
-
- raise ProfileNotFound(f"File {self.profile} does not exist in {storage['PROFILE_PATH']}")
- elif parsed_url.scheme in ('https', 'http'):
- return self.localize_path(self.profile)
- else:
- raise ProfileNotFound(f"Cannot handle scheme {parsed_url.scheme}")
-
- def load_instructions(self, namespace :Optional[str] = None) -> 'Script':
- if namespace:
- self.namespace = namespace
-
- self.spec = importlib.util.spec_from_file_location(self.namespace, self.path)
- imported = importlib.util.module_from_spec(self.spec)
- sys.modules[self.namespace] = imported
-
- return self
-
- def execute(self) -> ModuleType:
- if self.namespace not in sys.modules or self.spec is None:
- self.load_instructions()
-
- self.spec.loader.exec_module(sys.modules[self.namespace])
-
- return sys.modules[self.namespace]
-
-
-class Profile(Script):
- def __init__(self, installer :Optional[Installer], path :str):
- super(Profile, self).__init__(path, installer)
-
- def __dump__(self, *args :str, **kwargs :str) -> Dict[str, str]:
- return {'path': self.path}
-
- def __repr__(self, *args :str, **kwargs :str) -> str:
- return f'Profile({os.path.basename(self.profile)})'
-
- @property
- def name(self) -> str:
- return os.path.basename(self.profile)
-
- @property
- def is_desktop_profile(self) -> bool:
- return is_desktop_profile(repr(self))
-
- def install(self) -> ModuleType:
- # Before installing, revert any temporary changes to the namespace.
- # This ensures that the namespace during installation is the original initiation namespace.
- # (For instance awesome instead of aweosme.py or app-awesome.py)
- self.namespace = self.original_namespace
- return self.execute()
-
- def has_prep_function(self) -> bool:
- with open(self.path, 'r') as source:
- source_data = source.read()
-
- # Some crude safety checks, make sure the imported profile has
- # a __name__ check and if so, check if it's got a _prep_function()
- # we can call to ask for more user input.
- #
- # If the requirements are met, import with .py in the namespace to not
- # trigger a traditional:
- # if __name__ == 'moduleName'
- if '__name__' in source_data and '_prep_function' in source_data:
- with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
- if hasattr(imported, '_prep_function'):
- return True
- return False
-
- def has_post_install(self) -> bool:
- with open(self.path, 'r') as source:
- source_data = source.read()
-
- # Some crude safety checks, make sure the imported profile has
- # a __name__ check and if so, check if it's got a _prep_function()
- # we can call to ask for more user input.
- #
- # If the requirements are met, import with .py in the namespace to not
- # trigger a traditional:
- # if __name__ == 'moduleName'
- if '__name__' in source_data and '_post_install' in source_data:
- with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
- if hasattr(imported, '_post_install'):
- return True
-
- def is_top_level_profile(self) -> bool:
- with open(self.path, 'r') as source:
- source_data = source.read()
-
- if '__name__' in source_data and 'is_top_level_profile' in source_data:
- with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
- if hasattr(imported, 'is_top_level_profile'):
- return imported.is_top_level_profile
-
- # Default to True if nothing is specified,
- # since developers like less code - omitting it should assume they want to present it.
- return True
-
- def get_profile_description(self) -> str:
- with open(self.path, 'r') as source:
- source_data = source.read()
-
- if '__description__' in source_data:
- with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
- if hasattr(imported, '__description__'):
- return imported.__description__
-
- # Default to this string if the profile does not have a description.
- return "This profile does not have the __description__ attribute set."
-
- @property
- def packages(self) -> Optional[list]:
- """
- Returns a list of packages baked into the profile definition.
- If no package definition has been done, .packages() will return None.
- """
- with open(self.path, 'r') as source:
- source_data = source.read()
-
- # Some crude safety checks, make sure the imported profile has
- # a __name__ check before importing.
- #
- # If the requirements are met, import with .py in the namespace to not
- # trigger a traditional:
- # if __name__ == 'moduleName'
- if '__name__' in source_data and '__packages__' in source_data:
- with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
- if hasattr(imported, '__packages__'):
- return imported.__packages__
- return None
-
-
-class Application(Profile):
- def __repr__(self, *args :str, **kwargs :str):
- return f'Application({os.path.basename(self.profile)})'
-
- @property
- def path(self) -> str:
- parsed_url = urllib.parse.urlparse(self.profile)
-
- # The Profile was not a direct match on a remote URL
- if not parsed_url.scheme:
- # Try to locate all local or known URL's
- if not self.examples:
- self.examples = list_profiles(subpath='/applications')
-
- if f"{self.profile}" in self.examples:
- return self.localize_path(self.examples[self.profile]['path'])
- # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now:
- elif f"{self.profile}.py" in self.examples:
- return self.localize_path(self.examples[f"{self.profile}.py"]['path'])
-
- # Path was not found in any known examples, check if it's an absolute path
- if os.path.isfile(self.profile):
- return os.path.basename(self.profile)
-
- raise ProfileNotFound(f"Application file {self.profile} does not exist in {storage['PROFILE_PATH']}")
- elif parsed_url.scheme in ('https', 'http'):
- return self.localize_path(self.profile)
- else:
- raise ProfileNotFound(f"Application cannot handle scheme {parsed_url.scheme}")
-
- def install(self) -> ModuleType:
- # Before installing, revert any temporary changes to the namespace.
- # This ensures that the namespace during installation is the original initiation namespace.
- # (For instance awesome instead of aweosme.py or app-awesome.py)
- self.namespace = self.original_namespace
- return self.execute()
diff --git a/archinstall/lib/services.py b/archinstall/lib/services.py
deleted file mode 100644
index b177052b..00000000
--- a/archinstall/lib/services.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import os
-from .general import SysCommand
-
-
-def service_state(service_name: str) -> str:
- if os.path.splitext(service_name)[1] != '.service':
- service_name += '.service' # Just to be safe
-
- state = b''.join(SysCommand(f'systemctl show --no-pager -p SubState --value {service_name}', environment_vars={'SYSTEMD_COLORS': '0'}))
-
- return state.strip().decode('UTF-8')
diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py
index 8c358161..2f256e5d 100644
--- a/archinstall/lib/storage.py
+++ b/archinstall/lib/storage.py
@@ -1,26 +1,19 @@
-import os
-
# There's a few scenarios of execution:
-# 1. In the git repository, where ./profiles/ exist
+# 1. In the git repository, where ./profiles_bck/ exist
# 2. When executing from a remote directory, but targeted a script that starts from the git repository
-# 3. When executing as a python -m archinstall module where profiles exist one step back for library reasons.
+# 3. When executing as a python -m archinstall module where profiles_bck exist one step back for library reasons.
# (4. Added the ~/.config directory as an additional option for future reasons)
#
# And Keeping this in dict ensures that variables are shared across imports.
from typing import Any, Dict
+from pathlib import Path
+
storage: Dict[str, Any] = {
- 'PROFILE_PATH': [
- './profiles',
- '~/.config/archinstall/profiles',
- os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'profiles'),
- # os.path.abspath(f'{os.path.dirname(__file__)}/../examples')
- ],
- 'UPSTREAM_URL': 'https://raw.githubusercontent.com/archlinux/archinstall/master/profiles',
- 'PROFILE_DB': None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabbing.
- 'LOG_PATH': '/var/log/archinstall',
- 'LOG_FILE': 'install.log',
- 'MOUNT_POINT': '/mnt/archinstall',
+ 'PROFILE': Path(__file__).parent.parent.joinpath('default_profiles'),
+ 'LOG_PATH': Path('/var/log/archinstall'),
+ 'LOG_FILE': Path('install.log'),
+ 'MOUNT_POINT': Path('/mnt/archinstall'),
'ENC_IDENTIFIER': 'ainst',
'DISK_TIMEOUTS' : 1, # seconds
'DISK_RETRY_ATTEMPTS' : 5, # RETRY_ATTEMPTS * DISK_TIMEOUTS is used in disk operations
diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py
index 0d74f974..3ea4c70e 100644
--- a/archinstall/lib/translationhandler.py
+++ b/archinstall/lib/translationhandler.py
@@ -1,14 +1,14 @@
from __future__ import annotations
import json
-import logging
import os
import gettext
from dataclasses import dataclass
from pathlib import Path
from typing import List, Dict, Any, TYPE_CHECKING, Optional
-from .exceptions import TranslationError
+
+from .output import error, debug
if TYPE_CHECKING:
_: Any
@@ -80,8 +80,8 @@ class TranslationHandler:
language = Language(abbr, lang, translation, percent, translated_lang)
languages.append(language)
- except FileNotFoundError as error:
- raise TranslationError(f"Could not locate language file for '{lang}': {error}")
+ except FileNotFoundError as err:
+ raise FileNotFoundError(f"Could not locate language file for '{lang}': {err}")
return languages
@@ -89,12 +89,12 @@ class TranslationHandler:
"""
Set the provided font as the new terminal font
"""
- from .general import SysCommand, log
+ from .general import SysCommand
try:
- log(f'Setting font: {font}', level=logging.DEBUG)
+ debug(f'Setting font: {font}')
SysCommand(f'setfont {font}')
except Exception:
- log(f'Unable to set font {font}', level=logging.ERROR)
+ error(f'Unable to set font {font}')
def _load_language_mappings(self) -> List[Dict[str, Any]]:
"""
@@ -138,7 +138,7 @@ class TranslationHandler:
def get_language_by_abbr(self, abbr: str) -> Language:
"""
- Get a language object by its abbrevation, e.g. en
+ Get a language object by its abbreviation, e.g. en
"""
try:
return next(filter(lambda x: x.abbr == abbr, self._translated_languages))
@@ -168,7 +168,7 @@ class TranslationHandler:
translation_files = []
for filename in filenames:
- if len(filename) == 2 or filename == 'pt_BR':
+ if len(filename) == 2 or filename in ['pt_BR', 'zh-CN', 'zh-TW']:
translation_files.append(filename)
return translation_files
@@ -206,4 +206,4 @@ class DeferredTranslation:
@classmethod
def install(cls):
import builtins
- builtins._ = cls
+ builtins._ = cls # type: ignore
diff --git a/archinstall/lib/udev/__init__.py b/archinstall/lib/udev/__init__.py
deleted file mode 100644
index 86c8cc29..00000000
--- a/archinstall/lib/udev/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .udevadm import udevadm_info \ No newline at end of file
diff --git a/archinstall/lib/udev/udevadm.py b/archinstall/lib/udev/udevadm.py
deleted file mode 100644
index 84ec9cfd..00000000
--- a/archinstall/lib/udev/udevadm.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import typing
-import pathlib
-from ..general import SysCommand
-
-def udevadm_info(path :pathlib.Path) -> typing.Dict[str, str]:
- if path.resolve().exists() is False:
- return {}
-
- result = SysCommand(f"udevadm info {path.resolve()}")
- data = {}
- for line in result:
- if b': ' in line and b'=' in line:
- _, obj = line.split(b': ', 1)
- key, value = obj.split(b'=', 1)
- data[key.decode('UTF-8').lower()] = value.decode('UTF-8').strip()
-
- return data \ No newline at end of file
diff --git a/archinstall/lib/user_interaction/__init__.py b/archinstall/lib/user_interaction/__init__.py
deleted file mode 100644
index 2bc46759..00000000
--- a/archinstall/lib/user_interaction/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from .save_conf import save_config
-from .manage_users_conf import ask_for_additional_users
-from .backwards_compatible_conf import generic_select, generic_multi_select
-from .locale_conf import select_locale_lang, select_locale_enc
-from .system_conf import select_kernel, select_harddrives, select_driver, ask_for_bootloader, ask_for_swap
-from .network_conf import ask_to_configure_network
-from .partitioning_conf import select_partition
-from .general_conf import (ask_ntp, ask_for_a_timezone, ask_for_audio_selection, select_language, select_mirror_regions,
- select_profile, select_archinstall_language, ask_additional_packages_to_install,
- select_additional_repositories, ask_hostname, add_number_of_parrallel_downloads)
-from .disk_conf import ask_for_main_filesystem_format, select_individual_blockdevice_usage, select_disk_layout, select_disk
-from .utils import get_password, do_countdown
diff --git a/archinstall/lib/user_interaction/backwards_compatible_conf.py b/archinstall/lib/user_interaction/backwards_compatible_conf.py
deleted file mode 100644
index 296572d2..00000000
--- a/archinstall/lib/user_interaction/backwards_compatible_conf.py
+++ /dev/null
@@ -1,95 +0,0 @@
-from __future__ import annotations
-
-import logging
-import sys
-from collections.abc import Iterable
-from typing import Any, Union, TYPE_CHECKING
-
-from ..exceptions import RequirementError
-from ..menu import Menu
-from ..output import log
-
-if TYPE_CHECKING:
- _: Any
-
-
-def generic_select(
- p_options: Union[list, dict],
- input_text: str = '',
- allow_empty_input: bool = True,
- options_output: bool = True, # function not available
- sort: bool = False,
- multi: bool = False,
- default: Any = None) -> Any:
- """
- A generic select function that does not output anything
- other than the options and their indexes. As an example:
-
- generic_select(["first", "second", "third option"])
- > first
- second
- third option
- When the user has entered the option correctly,
- this function returns an item from list, a string, or None
-
- Options can be any iterable.
- Duplicate entries are not checked, but the results with them are unreliable. Which element to choose from the duplicates depends on the return of the index()
- Default value if not on the list of options will be added as the first element
- sort will be handled by Menu()
- """
- # We check that the options are iterable. If not we abort. Else we copy them to lists
- # it options is a dictionary we use the values as entries of the list
- # if options is a string object, each character becomes an entry
- # if options is a list, we implictily build a copy to maintain immutability
- if not isinstance(p_options, Iterable):
- log(f"Objects of type {type(p_options)} is not iterable, and are not supported at generic_select", fg="red")
- log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>", level=logging.WARNING)
- raise RequirementError("generic_select() requires an iterable as option.")
-
- input_text = input_text if input_text else _('Select one of the values shown below: ')
-
- if isinstance(p_options, dict):
- options = list(p_options.values())
- else:
- options = list(p_options)
- # check that the default value is in the list. If not it will become the first entry
- if default and default not in options:
- options.insert(0, default)
-
- # one of the drawbacks of the new interface is that in only allows string like options, so we do a conversion
- # also for the default value if it exists
- soptions = list(map(str, options))
- default_value = options[options.index(default)] if default else None
-
- selected_option = Menu(input_text,
- soptions,
- skip=allow_empty_input,
- multi=multi,
- default_option=default_value,
- sort=sort).run()
- # we return the original objects, not the strings.
- # options is the list with the original objects and soptions the list with the string values
- # thru the map, we get from the value selected in soptions it index, and thu it the original object
- if not selected_option:
- return selected_option
- elif isinstance(selected_option, list): # for multi True
- selected_option = list(map(lambda x: options[soptions.index(x)], selected_option))
- else: # for multi False
- selected_option = options[soptions.index(selected_option)]
- return selected_option
-
-
-def generic_multi_select(p_options: Union[list, dict],
- text: str = '',
- sort: bool = False,
- default: Any = None,
- allow_empty: bool = False) -> Any:
-
- text = text if text else _("Select one or more of the options below: ")
-
- return generic_select(p_options,
- input_text=text,
- allow_empty_input=allow_empty,
- sort=sort,
- multi=True,
- default=default)
diff --git a/archinstall/lib/user_interaction/disk_conf.py b/archinstall/lib/user_interaction/disk_conf.py
deleted file mode 100644
index 554d13ef..00000000
--- a/archinstall/lib/user_interaction/disk_conf.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from __future__ import annotations
-
-from typing import Any, Dict, TYPE_CHECKING, Optional
-
-from .partitioning_conf import manage_new_and_existing_partitions, get_default_partition_layout
-from ..disk import BlockDevice
-from ..exceptions import DiskError
-from ..menu import Menu
-from ..menu.menu import MenuSelectionType
-
-if TYPE_CHECKING:
- _: Any
-
-
-def ask_for_main_filesystem_format(advanced_options=False) -> str:
- options = {'btrfs': 'btrfs', 'ext4': 'ext4', 'xfs': 'xfs', 'f2fs': 'f2fs'}
-
- advanced = {'ntfs': 'ntfs'}
-
- if advanced_options:
- options.update(advanced)
-
- prompt = _('Select which filesystem your main partition should use')
- choice = Menu(prompt, options, skip=False).run()
- return choice.value
-
-
-def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]:
- result = {}
-
- for device in block_devices:
- layout = manage_new_and_existing_partitions(device)
- result[device.path] = layout
-
- return result
-
-
-def select_disk_layout(preset: Optional[Dict[str, Any]], block_devices: list, advanced_options=False) -> Optional[Dict[str, Any]]:
- wipe_mode = str(_('Wipe all selected drives and use a best-effort default partition layout'))
- custome_mode = str(_('Select what to do with each individual drive (followed by partition usage)'))
- modes = [wipe_mode, custome_mode]
-
- warning = str(_('Are you sure you want to reset this setting?'))
-
- choice = Menu(
- _('Select what you wish to do with the selected block devices'),
- modes,
- allow_reset=True,
- allow_reset_warning_msg=warning
- ).run()
-
- match choice.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Reset: return None
- case MenuSelectionType.Selection:
- if choice.value == wipe_mode:
- return get_default_partition_layout(block_devices, advanced_options)
- else:
- return select_individual_blockdevice_usage(block_devices)
-
-
-def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> Optional[BlockDevice]:
- """
- Asks the user to select a harddrive from the `dict_o_disks` selection.
- Usually this is combined with :ref:`archinstall.list_drives`.
-
- :param dict_o_disks: A `dict` where keys are the drive-name, value should be a dict containing drive information.
- :type dict_o_disks: dict
-
- :return: The name/path (the dictionary key) of the selected drive
- :rtype: str
- """
- drives = sorted(list(dict_o_disks.keys()))
- if len(drives) >= 1:
- title = str(_('You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)')) + '\n'
- title += str(_('Select one of the disks or skip and use /mnt as default'))
-
- choice = Menu(title, drives).run()
-
- if choice.type_ == MenuSelectionType.Skip:
- return None
-
- drive = dict_o_disks[choice.value]
- return drive
-
- raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.')
diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py
deleted file mode 100644
index fc7ded45..00000000
--- a/archinstall/lib/user_interaction/general_conf.py
+++ /dev/null
@@ -1,271 +0,0 @@
-from __future__ import annotations
-
-import logging
-import pathlib
-from typing import List, Any, Optional, Dict, TYPE_CHECKING
-
-from ..locale_helpers import list_keyboard_languages, list_timezones
-from ..menu import Menu
-from ..menu.menu import MenuSelectionType
-from ..menu.text_input import TextInput
-from ..mirrors import list_mirrors
-from ..output import log
-from ..packages.packages import validate_package_list
-from ..profiles import Profile, list_profiles
-from ..storage import storage
-from ..translationhandler import Language
-
-if TYPE_CHECKING:
- _: Any
-
-
-def ask_ntp(preset: bool = True) -> bool:
- prompt = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n'))
- prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor more information, please check the Arch wiki'))
- if preset:
- preset_val = Menu.yes()
- else:
- preset_val = Menu.no()
- choice = Menu(prompt, Menu.yes_no(), skip=False, preset_values=preset_val, default_option=Menu.yes()).run()
-
- return False if choice.value == Menu.no() else True
-
-
-def ask_hostname(preset: str = None) -> str:
- hostname = TextInput(_('Desired hostname for the installation: '), preset).run().strip(' ')
- return hostname
-
-
-def ask_for_a_timezone(preset: str = None) -> str:
- timezones = list_timezones()
- default = 'UTC'
-
- choice = Menu(
- _('Select a timezone'),
- list(timezones),
- preset_values=preset,
- default_option=default
- ).run()
-
- match choice.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Selection: return choice.value
-
-
-def ask_for_audio_selection(desktop: bool = True, preset: str = None) -> str:
- no_audio = str(_('No audio server'))
- choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', no_audio]
- default = 'pipewire' if desktop else no_audio
-
- choice = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=default).run()
-
- match choice.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Selection: return choice.value
-
-
-def select_language(preset_value: str = None) -> str:
- """
- Asks the user to select a language
- Usually this is combined with :ref:`archinstall.list_keyboard_languages`.
-
- :return: The language/dictionary key of the selected language
- :rtype: str
- """
- kb_lang = list_keyboard_languages()
- # sort alphabetically and then by length
- sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len)
-
- selected_lang = Menu(
- _('Select keyboard layout'),
- sorted_kb_lang,
- preset_values=preset_value,
- sort=False
- ).run()
-
- if selected_lang.value is None:
- return preset_value
-
- return selected_lang.value
-
-
-def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]:
- """
- Asks the user to select a mirror or region
- Usually this is combined with :ref:`archinstall.list_mirrors`.
-
- :return: The dictionary information about a mirror/region.
- :rtype: dict
- """
- if preset_values is None:
- preselected = None
- else:
- preselected = list(preset_values.keys())
- mirrors = list_mirrors()
- selected_mirror = Menu(
- _('Select one of the regions to download packages from'),
- list(mirrors.keys()),
- preset_values=preselected,
- multi=True,
- allow_reset=True
- ).run()
-
- match selected_mirror.type_:
- case MenuSelectionType.Reset: return {}
- case MenuSelectionType.Skip: return preset_values
- case _: return {selected: mirrors[selected] for selected in selected_mirror.value}
-
-
-def select_archinstall_language(languages: List[Language], preset_value: Language) -> Language:
- # these are the displayed language names which can either be
- # the english name of a language or, if present, the
- # name of the language in its own language
- options = {lang.display_name: lang for lang in languages}
-
- title = 'NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n'
- title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n'
- title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n'
-
- choice = Menu(
- title,
- list(options.keys()),
- default_option=preset_value.display_name,
- preview_size=0.5
- ).run()
-
- match choice.type_:
- case MenuSelectionType.Skip:
- return preset_value
- case MenuSelectionType.Selection:
- return options[choice.value]
-
-
-def select_profile(preset) -> Optional[Profile]:
- """
- # Asks the user to select a profile from the available profiles.
- #
- # :return: The name/dictionary key of the selected profile
- # :rtype: str
- # """
- top_level_profiles = sorted(list(list_profiles(filter_top_level_profiles=True)))
- options = {}
-
- for profile in top_level_profiles:
- profile = Profile(None, profile)
- description = profile.get_profile_description()
-
- option = f'{profile.profile}: {description}'
- options[option] = profile
-
- title = _('This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments')
- warning = str(_('Are you sure you want to reset this setting?'))
-
- selection = Menu(
- title=title,
- p_options=list(options.keys()),
- allow_reset=True,
- allow_reset_warning_msg=warning
- ).run()
-
- match selection.type_:
- case MenuSelectionType.Selection:
- return options[selection.value] if selection.value is not None else None
- case MenuSelectionType.Reset:
- storage['profile_minimal'] = False
- storage['_selected_servers'] = []
- storage['_desktop_profile'] = None
- storage['sway_sys_priv_ctrl'] = None
- storage['arguments']['sway_sys_priv_ctrl'] = None
- storage['arguments']['desktop-environment'] = None
- storage['arguments']['gfx_driver'] = None
- storage['arguments']['gfx_driver_packages'] = None
- return None
- case MenuSelectionType.Skip:
- return None
-
-
-def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]:
- # Additional packages (with some light weight error handling for invalid package names)
- print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.'))
- print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.'))
-
- def read_packages(already_defined: list = []) -> list:
- display = ' '.join(already_defined)
- input_packages = TextInput(_('Write additional packages to install (space separated, leave blank to skip): '), display).run().strip()
- return input_packages.split() if input_packages else []
-
- pre_set_packages = pre_set_packages if pre_set_packages else []
- packages = read_packages(pre_set_packages)
-
- if not storage['arguments']['offline'] and not storage['arguments']['no_pkg_lookups']:
- while True:
- if len(packages):
- # Verify packages that were given
- print(_("Verifying that additional packages exist (this might take a few seconds)"))
- valid, invalid = validate_package_list(packages)
-
- if invalid:
- log(f"Some packages could not be found in the repository: {invalid}", level=logging.WARNING, fg='red')
- packages = read_packages(valid)
- continue
- break
-
- return packages
-
-
-def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Optional[int]:
- max_downloads = 5
- print(_(f"This option enables the number of parallel downloads that can occur during installation"))
- print(_(f"Enter the number of parallel downloads to be enabled.\n (Enter a value between 1 to {max_downloads})\nNote:"))
- print(_(f" - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"))
- print(_(f" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"))
- print(_(f" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"))
-
- while True:
- try:
- input_number = int(TextInput(_("[Default value: 0] > ")).run().strip() or 0)
- if input_number <= 0:
- input_number = 0
- elif input_number > max_downloads:
- input_number = max_downloads
- break
- except:
- print(_(f"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"))
-
- pacman_conf_path = pathlib.Path("/etc/pacman.conf")
- with pacman_conf_path.open() as f:
- pacman_conf = f.read().split("\n")
-
- with pacman_conf_path.open("w") as fwrite:
- for line in pacman_conf:
- if "ParallelDownloads" in line:
- fwrite.write(f"ParallelDownloads = {input_number+1}\n") if not input_number == 0 else fwrite.write("#ParallelDownloads = 0\n")
- else:
- fwrite.write(f"{line}\n")
-
- return input_number
-
-
-def select_additional_repositories(preset: List[str]) -> List[str]:
- """
- Allows the user to select additional repositories (multilib, and testing) if desired.
-
- :return: The string as a selected repository
- :rtype: string
- """
-
- repositories = ["multilib", "testing"]
-
- choice = Menu(
- _('Choose which optional additional repositories to enable'),
- repositories,
- sort=False,
- multi=True,
- preset_values=preset,
- allow_reset=True
- ).run()
-
- match choice.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Reset: return []
- case MenuSelectionType.Selection: return choice.value
diff --git a/archinstall/lib/user_interaction/locale_conf.py b/archinstall/lib/user_interaction/locale_conf.py
deleted file mode 100644
index bbbe070b..00000000
--- a/archinstall/lib/user_interaction/locale_conf.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from __future__ import annotations
-
-from typing import Any, TYPE_CHECKING
-
-from ..locale_helpers import list_locales
-from ..menu import Menu
-from ..menu.menu import MenuSelectionType
-
-if TYPE_CHECKING:
- _: Any
-
-
-def select_locale_lang(preset: str = None) -> str:
- locales = list_locales()
- locale_lang = set([locale.split()[0] for locale in locales])
-
- selected_locale = Menu(
- _('Choose which locale language to use'),
- list(locale_lang),
- sort=True,
- preset_values=preset
- ).run()
-
- match selected_locale.type_:
- case MenuSelectionType.Selection: return selected_locale.value
- case MenuSelectionType.Skip: return preset
-
-
-def select_locale_enc(preset: str = None) -> str:
- locales = list_locales()
- locale_enc = set([locale.split()[1] for locale in locales])
-
- selected_locale = Menu(
- _('Choose which locale encoding to use'),
- list(locale_enc),
- sort=True,
- preset_values=preset
- ).run()
-
- match selected_locale.type_:
- case MenuSelectionType.Selection: return selected_locale.value
- case MenuSelectionType.Skip: return preset
diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py
deleted file mode 100644
index 0a5ede51..00000000
--- a/archinstall/lib/user_interaction/partitioning_conf.py
+++ /dev/null
@@ -1,362 +0,0 @@
-from __future__ import annotations
-
-import copy
-from typing import List, Any, Dict, Union, TYPE_CHECKING, Callable, Optional
-
-from ..menu import Menu
-from ..menu.menu import MenuSelectionType
-from ..output import log, FormattedOutput
-
-from ..disk.validators import fs_types
-
-if TYPE_CHECKING:
- from ..disk import BlockDevice
- from ..disk.partition import Partition
- _: Any
-
-
-def partition_overlap(partitions: list, start: str, end: str) -> bool:
- # TODO: Implement sanity check
- return False
-
-
-def current_partition_layout(partitions: List[Dict[str, Any]], with_idx: bool = False, with_title: bool = True) -> str:
-
- def do_padding(name: str, max_len: int):
- spaces = abs(len(str(name)) - max_len) + 2
- pad_left = int(spaces / 2)
- pad_right = spaces - pad_left
- return f'{pad_right * " "}{name}{pad_left * " "}|'
-
- def flatten_data(data: Dict[str, Any]) -> Dict[str, Any]:
- flattened = {}
- for k, v in data.items():
- if k == 'filesystem':
- flat = flatten_data(v)
- flattened.update(flat)
- elif k == 'btrfs':
- # we're going to create a separate table for the btrfs subvolumes
- pass
- else:
- flattened[k] = v
- return flattened
-
- display_data: List[Dict[str, Any]] = [flatten_data(entry) for entry in partitions]
-
- column_names = {}
-
- # this will add an initial index to the table for each partition
- if with_idx:
- column_names['index'] = max([len(str(len(display_data))), len('index')])
-
- # determine all attribute names and the max length
- # of the value among all display_data to know the width
- # of the table cells
- for p in display_data:
- for attribute, value in p.items():
- if attribute in column_names.keys():
- column_names[attribute] = max([column_names[attribute], len(str(value)), len(attribute)])
- else:
- column_names[attribute] = max([len(str(value)), len(attribute)])
-
- current_layout = ''
- for name, max_len in column_names.items():
- current_layout += do_padding(name, max_len)
-
- current_layout = f'{current_layout[:-1]}\n{"-" * len(current_layout)}\n'
-
- for idx, p in enumerate(display_data):
- row = ''
- for name, max_len in column_names.items():
- if name == 'index':
- row += do_padding(str(idx), max_len)
- elif name in p:
- row += do_padding(p[name], max_len)
- else:
- row += ' ' * (max_len + 2) + '|'
-
- current_layout += f'{row[:-1]}\n'
-
- # we'll create a separate table for the btrfs subvolumes
- btrfs_subvolumes = [partition['btrfs']['subvolumes'] for partition in partitions if partition.get('btrfs', None)]
- if len(btrfs_subvolumes) > 0:
- for subvolumes in btrfs_subvolumes:
- output = FormattedOutput.as_table(subvolumes)
- current_layout += f'\n{output}'
-
- if with_title:
- title = str(_('Current partition layout'))
- return f'\n\n{title}:\n\n{current_layout}'
-
- return current_layout
-
-
-def _get_partitions(partitions :List[Partition], filter_ :Callable = None) -> List[str]:
- """
- filter allows to filter out the indexes once they are set. Should return True if element is to be included
- """
- partition_indexes = []
- for i in range(len(partitions)):
- if filter_:
- if filter_(partitions[i]):
- partition_indexes.append(str(i))
- else:
- partition_indexes.append(str(i))
-
- return partition_indexes
-
-
-def select_partition(
- title :str,
- partitions :List[Partition],
- multiple :bool = False,
- filter_ :Callable = None
-) -> Optional[int, List[int]]:
- partition_indexes = _get_partitions(partitions, filter_)
-
- if len(partition_indexes) == 0:
- return None
-
- choice = Menu(title, partition_indexes, multi=multiple).run()
-
- if choice.type_ == MenuSelectionType.Skip:
- return None
-
- if isinstance(choice.value, list):
- return [int(p) for p in choice.value]
- else:
- return int(choice.value)
-
-
-def get_default_partition_layout(
- block_devices: Union['BlockDevice', List['BlockDevice']],
- advanced_options: bool = False
-) -> Optional[Dict[str, Any]]:
- from ..disk import suggest_single_disk_layout, suggest_multi_disk_layout
-
- if len(block_devices) == 1:
- return suggest_single_disk_layout(block_devices[0], advanced_options=advanced_options)
- else:
- return suggest_multi_disk_layout(block_devices, advanced_options=advanced_options)
-
-
-def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, Any]: # noqa: max-complexity: 50
- block_device_struct = {"partitions": [partition.__dump__() for partition in block_device.partitions.values()]}
- original_layout = copy.deepcopy(block_device_struct)
-
- new_partition = str(_('Create a new partition'))
- suggest_partition_layout = str(_('Suggest partition layout'))
- delete_partition = str(_('Delete a partition'))
- delete_all_partitions = str(_('Clear/Delete all partitions'))
- assign_mount_point = str(_('Assign mount-point for a partition'))
- mark_formatted = str(_('Mark/Unmark a partition to be formatted (wipes data)'))
- mark_compressed = str(_('Mark/Unmark a partition as compressed (btrfs only)'))
- mark_bootable = str(_('Mark/Unmark a partition as bootable (automatic for /boot)'))
- set_filesystem_partition = str(_('Set desired filesystem for a partition'))
- set_btrfs_subvolumes = str(_('Set desired subvolumes on a btrfs partition'))
- save_and_exit = str(_('Save and exit'))
- cancel = str(_('Cancel'))
-
- while True:
- modes = [new_partition, suggest_partition_layout]
-
- if len(block_device_struct['partitions']) > 0:
- modes += [
- delete_partition,
- delete_all_partitions,
- assign_mount_point,
- mark_formatted,
- mark_bootable,
- mark_compressed,
- set_filesystem_partition,
- ]
-
- indexes = _get_partitions(
- block_device_struct["partitions"],
- filter_=lambda x: True if x.get('filesystem', {}).get('format') == 'btrfs' else False
- )
-
- if len(indexes) > 0:
- modes += [set_btrfs_subvolumes]
-
- title = _('Select what to do with\n{}').format(block_device)
-
- # show current partition layout:
- if len(block_device_struct["partitions"]):
- title += current_partition_layout(block_device_struct['partitions']) + '\n'
-
- modes += [save_and_exit, cancel]
-
- task = Menu(title, modes, sort=False, skip=False).run()
- task = task.value
-
- if task == cancel:
- return original_layout
- elif task == save_and_exit:
- break
-
- if task == new_partition:
- from ..disk import valid_parted_position
-
- # if partition_type == 'gpt':
- # # https://www.gnu.org/software/parted/manual/html_node/mkpart.html
- # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html
- # name = input("Enter a desired name for the partition: ").strip()
-
- fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run()
-
- if fs_choice.type_ == MenuSelectionType.Skip:
- continue
-
- prompt = str(_('Enter the start location (in parted units: s, GB, %, etc. ; default: {}): ')).format(
- block_device.first_free_sector
- )
- start = input(prompt).strip()
-
- if not start.strip():
- start = block_device.first_free_sector
- end_suggested = block_device.first_end_sector
- else:
- end_suggested = '100%'
-
- prompt = str(_('Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): ')).format(
- end_suggested
- )
- end = input(prompt).strip()
-
- if not end.strip():
- end = end_suggested
-
- if valid_parted_position(start) and valid_parted_position(end):
- if partition_overlap(block_device_struct["partitions"], start, end):
- log(f"This partition overlaps with other partitions on the drive! Ignoring this partition creation.",
- fg="red")
- continue
-
- block_device_struct["partitions"].append({
- "type": "primary", # Strictly only allowed under MS-DOS, but GPT accepts it so it's "safe" to inject
- "start": start,
- "size": end,
- "mountpoint": None,
- "wipe": True,
- "filesystem": {
- "format": fs_choice.value
- }
- })
- else:
- log(f"Invalid start ({valid_parted_position(start)}) or end ({valid_parted_position(end)}) for this partition. Ignoring this partition creation.",
- fg="red")
- continue
- elif task == suggest_partition_layout:
- from ..disk import suggest_single_disk_layout
-
- if len(block_device_struct["partitions"]):
- prompt = _('{}\ncontains queued partitions, this will remove those, are you sure?').format(block_device)
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run()
-
- if choice.value == Menu.no():
- continue
-
- block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path])
- else:
- current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True)
-
- if task == delete_partition:
- title = _('{}\n\nSelect by index which partitions to delete').format(current_layout)
- to_delete = select_partition(title, block_device_struct["partitions"], multiple=True)
-
- if to_delete:
- block_device_struct['partitions'] = [
- p for idx, p in enumerate(block_device_struct['partitions']) if idx not in to_delete
- ]
- elif task == mark_compressed:
- title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout)
- partition = select_partition(title, block_device_struct["partitions"])
-
- if partition is not None:
- if "filesystem" not in block_device_struct["partitions"][partition]:
- block_device_struct["partitions"][partition]["filesystem"] = {}
- if "mount_options" not in block_device_struct["partitions"][partition]["filesystem"]:
- block_device_struct["partitions"][partition]["filesystem"]["mount_options"] = []
-
- if "compress=zstd" not in block_device_struct["partitions"][partition]["filesystem"]["mount_options"]:
- block_device_struct["partitions"][partition]["filesystem"]["mount_options"].append("compress=zstd")
- elif task == delete_all_partitions:
- block_device_struct["partitions"] = []
- block_device_struct["wipe"] = True
- elif task == assign_mount_point:
- title = _('{}\n\nSelect by index which partition to mount where').format(current_layout)
- partition = select_partition(title, block_device_struct["partitions"])
-
- if partition is not None:
- print(_(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.'))
- mountpoint = input(_('Select where to mount partition (leave blank to remove mountpoint): ')).strip()
-
- if len(mountpoint):
- block_device_struct["partitions"][partition]['mountpoint'] = mountpoint
- if mountpoint == '/boot':
- log(f"Marked partition as bootable because mountpoint was set to /boot.", fg="yellow")
- block_device_struct["partitions"][partition]['boot'] = True
- else:
- del (block_device_struct["partitions"][partition]['mountpoint'])
-
- elif task == mark_formatted:
- title = _('{}\n\nSelect which partition to mask for formatting').format(current_layout)
- partition = select_partition(title, block_device_struct["partitions"])
-
- if partition is not None:
- # If we mark a partition for formatting, but the format is CRYPTO LUKS, there's no point in formatting it really
- # without asking the user which inner-filesystem they want to use. Since the flag 'encrypted' = True is already set,
- # it's safe to change the filesystem for this partition.
- if block_device_struct["partitions"][partition].get('filesystem',{}).get('format', 'crypto_LUKS') == 'crypto_LUKS':
- if not block_device_struct["partitions"][partition].get('filesystem', None):
- block_device_struct["partitions"][partition]['filesystem'] = {}
-
- fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run()
-
- if fs_choice.type_ == MenuSelectionType.Selection:
- block_device_struct["partitions"][partition]['filesystem']['format'] = fs_choice.value
-
- # Negate the current wipe marking
- block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False)
-
- elif task == mark_bootable:
- title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout)
- partition = select_partition(title, block_device_struct["partitions"])
-
- if partition is not None:
- block_device_struct["partitions"][partition]['boot'] = \
- not block_device_struct["partitions"][partition].get('boot', False)
-
- elif task == set_filesystem_partition:
- title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout)
- partition = select_partition(title, block_device_struct["partitions"])
-
- if partition is not None:
- if not block_device_struct["partitions"][partition].get('filesystem', None):
- block_device_struct["partitions"][partition]['filesystem'] = {}
-
- fstype_title = _('Enter a desired filesystem type for the partition: ')
- fs_choice = Menu(fstype_title, fs_types()).run()
-
- if fs_choice.type_ == MenuSelectionType.Selection:
- block_device_struct["partitions"][partition]['filesystem']['format'] = fs_choice.value
-
- elif task == set_btrfs_subvolumes:
- from .subvolume_config import SubvolumeList
-
- # TODO get preexisting partitions
- title = _('{}\n\nSelect which partition to set subvolumes on').format(current_layout)
- partition = select_partition(title, block_device_struct["partitions"],filter_=lambda x:True if x.get('filesystem',{}).get('format') == 'btrfs' else False)
-
- if partition is not None:
- if not block_device_struct["partitions"][partition].get('btrfs', {}):
- block_device_struct["partitions"][partition]['btrfs'] = {}
- if not block_device_struct["partitions"][partition]['btrfs'].get('subvolumes', []):
- block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = []
-
- prev = block_device_struct["partitions"][partition]['btrfs']['subvolumes']
- result = SubvolumeList(_("Manage btrfs subvolumes for current partition"), prev).run()
- block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = result
-
- return block_device_struct
diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py
deleted file mode 100644
index 5b4ae2b3..00000000
--- a/archinstall/lib/user_interaction/save_conf.py
+++ /dev/null
@@ -1,135 +0,0 @@
-from __future__ import annotations
-
-import logging
-
-from pathlib import Path
-from typing import Any, Dict, TYPE_CHECKING
-
-from ..configuration import ConfigurationOutput
-from ..general import SysCommand
-from ..menu import Menu
-from ..menu.menu import MenuSelectionType
-from ..output import log
-
-if TYPE_CHECKING:
- _: Any
-
-
-def save_config(config: Dict):
-
- def preview(selection: str):
- if options['user_config'] == selection:
- json_config = config_output.user_config_to_json()
- return f'{config_output.user_configuration_file}\n{json_config}'
- elif options['user_creds'] == selection:
- if json_config := config_output.user_credentials_to_json():
- return f'{config_output.user_credentials_file}\n{json_config}'
- else:
- return str(_('No configuration'))
- elif options['disk_layout'] == selection:
- if json_config := config_output.disk_layout_to_json():
- return f'{config_output.disk_layout_file}\n{json_config}'
- else:
- return str(_('No configuration'))
- elif options['all'] == selection:
- output = f'{config_output.user_configuration_file}\n'
- if json_config := config_output.user_credentials_to_json():
- output += f'{config_output.user_credentials_file}\n'
- if json_config := config_output.disk_layout_to_json():
- output += f'{config_output.disk_layout_file}\n'
- return output[:-1]
- return None
-
- config_output = ConfigurationOutput(config)
-
- options = {
- 'user_config': str(_('Save user configuration')),
- 'user_creds': str(_('Save user credentials')),
- 'disk_layout': str(_('Save disk layout')),
- 'all': str(_('Save all'))
- }
-
- choice = Menu(
- _('Choose which configuration to save'),
- list(options.values()),
- sort=False,
- skip=True,
- preview_size=0.75,
- preview_command=preview
- ).run()
-
- if choice.type_ == MenuSelectionType.Skip:
- return
-
- dirs_to_exclude = [
- '/bin',
- '/dev',
- '/lib',
- '/lib64',
- '/lost+found',
- '/opt',
- '/proc',
- '/run',
- '/sbin',
- '/srv',
- '/sys',
- '/usr',
- '/var',
- ]
- log(
- _('When picking a directory to save configuration files to,'
- ' by default we will ignore the following folders: ') + ','.join(dirs_to_exclude),
- level=logging.DEBUG
- )
-
- log(_('Finding possible directories to save configuration files ...'), level=logging.INFO)
-
- find_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune '
- file_picker_command = f'find / {find_exclude} -o -type d -print0'
- possible_save_dirs = list(
- filter(None, SysCommand(file_picker_command).decode().split('\x00'))
- )
-
- selection = Menu(
- _('Select directory (or directories) for saving configuration files'),
- possible_save_dirs,
- multi=True,
- skip=True,
- allow_reset=False,
- ).run()
-
- match selection.type_:
- case MenuSelectionType.Skip:
- return
- case _:
- save_dirs = selection.value
-
- prompt = _('Do you want to save {} configuration file(s) in the following locations?\n\n{}').format(
- list(options.keys())[list(options.values()).index(choice.value)],
- save_dirs
- )
- save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
- if save_confirmation == Menu.no():
- return
-
- log(
- _('Saving {} configuration files to {}').format(
- list(options.keys())[list(options.values()).index(choice.value)],
- save_dirs
- ),
- level=logging.DEBUG
- )
-
- if save_dirs is not None:
- for save_dir_str in save_dirs:
- save_dir = Path(save_dir_str)
- if options['user_config'] == choice.value:
- config_output.save_user_config(save_dir)
- elif options['user_creds'] == choice.value:
- config_output.save_user_creds(save_dir)
- elif options['disk_layout'] == choice.value:
- config_output.save_disk_layout(save_dir)
- elif options['all'] == choice.value:
- config_output.save_user_config(save_dir)
- config_output.save_user_creds(save_dir)
- config_output.save_disk_layout(save_dir)
diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py
deleted file mode 100644
index 94150dee..00000000
--- a/archinstall/lib/user_interaction/subvolume_config.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from typing import Dict, List, Optional, Any, TYPE_CHECKING
-
-from ..menu.list_manager import ListManager
-from ..menu.menu import MenuSelectionType
-from ..menu.text_input import TextInput
-from ..menu import Menu
-from ..models.subvolume import Subvolume
-from ... import FormattedOutput
-
-if TYPE_CHECKING:
- _: Any
-
-
-class SubvolumeList(ListManager):
- def __init__(self, prompt: str, subvolumes: List[Subvolume]):
- self._actions = [
- str(_('Add subvolume')),
- str(_('Edit subvolume')),
- str(_('Delete subvolume'))
- ]
- super().__init__(prompt, subvolumes, [self._actions[0]], self._actions[1:])
-
- def reformat(self, data: List[Subvolume]) -> Dict[str, Optional[Subvolume]]:
- table = FormattedOutput.as_table(data)
- rows = table.split('\n')
-
- # these are the header rows of the table and do not map to any User obviously
- # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
- # the selectable rows so the header has to be aligned
- display_data: Dict[str, Optional[Subvolume]] = {f' {rows[0]}': None, f' {rows[1]}': None}
-
- for row, subvol in zip(rows[2:], data):
- row = row.replace('|', '\\|')
- display_data[row] = subvol
-
- return display_data
-
- def selected_action_display(self, subvolume: Subvolume) -> str:
- return subvolume.name
-
- def _prompt_options(self, editing: Optional[Subvolume] = None) -> List[str]:
- preset_options = []
- if editing:
- preset_options = editing.options
-
- choice = Menu(
- str(_("Select the desired subvolume options ")),
- ['nodatacow','compress'],
- skip=True,
- preset_values=preset_options,
- multi=True
- ).run()
-
- if choice.type_ == MenuSelectionType.Selection:
- return choice.value # type: ignore
-
- return []
-
- def _add_subvolume(self, editing: Optional[Subvolume] = None) -> Optional[Subvolume]:
- name = TextInput(f'\n\n{_("Subvolume name")}: ', editing.name if editing else '').run()
-
- if not name:
- return None
-
- mountpoint = TextInput(f'\n{_("Subvolume mountpoint")}: ', editing.mountpoint if editing else '').run()
-
- if not mountpoint:
- return None
-
- options = self._prompt_options(editing)
-
- subvolume = Subvolume(name, mountpoint)
- subvolume.compress = 'compress' in options
- subvolume.nodatacow = 'nodatacow' in options
-
- return subvolume
-
- def handle_action(self, action: str, entry: Optional[Subvolume], data: List[Subvolume]) -> List[Subvolume]:
- if action == self._actions[0]: # add
- new_subvolume = self._add_subvolume()
-
- if new_subvolume is not None:
- # in case a user with the same username as an existing user
- # was created we'll replace the existing one
- data = [d for d in data if d.name != new_subvolume.name]
- data += [new_subvolume]
- elif entry is not None:
- if action == self._actions[1]: # edit subvolume
- new_subvolume = self._add_subvolume(entry)
-
- if new_subvolume is not None:
- # we'll remove the original subvolume and add the modified version
- data = [d for d in data if d.name != entry.name and d.name != new_subvolume.name]
- data += [new_subvolume]
- elif action == self._actions[2]: # delete
- data = [d for d in data if d != entry]
-
- return data
diff --git a/archinstall/lib/user_interaction/system_conf.py b/archinstall/lib/user_interaction/system_conf.py
deleted file mode 100644
index e1581677..00000000
--- a/archinstall/lib/user_interaction/system_conf.py
+++ /dev/null
@@ -1,168 +0,0 @@
-from __future__ import annotations
-
-from typing import List, Any, Dict, TYPE_CHECKING
-
-from ..disk import all_blockdevices
-from ..exceptions import RequirementError
-from ..hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_intel_graphics, has_nvidia_graphics
-from ..menu import Menu
-from ..menu.menu import MenuSelectionType
-from ..storage import storage
-
-if TYPE_CHECKING:
- _: Any
-
-
-def select_kernel(preset: List[str] = None) -> List[str]:
- """
- Asks the user to select a kernel for system.
-
- :return: The string as a selected kernel
- :rtype: string
- """
-
- kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"]
- default_kernel = "linux"
-
- warning = str(_('Are you sure you want to reset this setting?'))
-
- choice = Menu(
- _('Choose which kernels to use or leave blank for default "{}"').format(default_kernel),
- kernels,
- sort=True,
- multi=True,
- preset_values=preset,
- allow_reset=True,
- allow_reset_warning_msg=warning
- ).run()
-
- match choice.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Reset: return []
- case MenuSelectionType.Selection: return choice.value
-
-
-def select_harddrives(preset: List[str] = []) -> List[str]:
- """
- Asks the user to select one or multiple hard drives
-
- :return: List of selected hard drives
- :rtype: list
- """
- hard_drives = all_blockdevices(partitions=False).values()
- options = {f'{option}': option for option in hard_drives}
-
- title = str(_('Select one or more hard drives to use and configure\n'))
- title += str(_('Any modifications to the existing setting will reset the disk layout!'))
-
- warning = str(_('If you reset the harddrive selection this will also reset the current disk layout. Are you sure?'))
-
- selected_harddrive = Menu(
- title,
- list(options.keys()),
- multi=True,
- allow_reset=True,
- allow_reset_warning_msg=warning
- ).run()
-
- match selected_harddrive.type_:
- case MenuSelectionType.Reset: return []
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Selection: return [options[i] for i in selected_harddrive.value]
-
-
-def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str:
- """
- Some what convoluted function, whose job is simple.
- Select a graphics driver from a pre-defined set of popular options.
-
- (The template xorg is for beginner users, not advanced, and should
- there for appeal to the general public first and edge cases later)
- """
-
- drivers = sorted(list(options))
-
- if drivers:
- arguments = storage.get('arguments', {})
- title = ''
-
- if has_amd_graphics():
- title += str(_(
- 'For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.'
- )) + '\n'
- if has_intel_graphics():
- title += str(_(
- 'For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n'
- ))
- if has_nvidia_graphics():
- title += str(_(
- 'For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n'
- ))
-
- title += str(_('\n\nSelect a graphics driver or leave blank to install all open-source drivers'))
- choice = Menu(title, drivers).run()
-
- if choice.type_ != MenuSelectionType.Selection:
- return arguments.get('gfx_driver')
-
- arguments['gfx_driver'] = choice.value
- return options.get(choice.value)
-
- raise RequirementError("Selecting drivers require a least one profile to be given as an option.")
-
-
-def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> str:
- if preset == 'systemd-bootctl':
- preset_val = 'systemd-boot' if advanced_options else Menu.no()
- elif preset == 'grub-install':
- preset_val = 'grub' if advanced_options else Menu.yes()
- else:
- preset_val = preset
-
- bootloader = "systemd-bootctl" if has_uefi() else "grub-install"
-
- if has_uefi():
- if not advanced_options:
- selection = Menu(
- _('Would you like to use GRUB as a bootloader instead of systemd-boot?'),
- Menu.yes_no(),
- preset_values=preset_val,
- default_option=Menu.no()
- ).run()
-
- match selection.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Selection: bootloader = 'grub-install' if selection.value == Menu.yes() else bootloader
- else:
- # We use the common names for the bootloader as the selection, and map it back to the expected values.
- choices = ['systemd-boot', 'grub', 'efistub']
- selection = Menu(_('Choose a bootloader'), choices, preset_values=preset_val).run()
-
- value = ''
- match selection.type_:
- case MenuSelectionType.Skip: value = preset_val
- case MenuSelectionType.Selection: value = selection.value
-
- if value != "":
- if value == 'systemd-boot':
- bootloader = 'systemd-bootctl'
- elif value == 'grub':
- bootloader = 'grub-install'
- else:
- bootloader = value
-
- return bootloader
-
-
-def ask_for_swap(preset: bool = True) -> bool:
- if preset:
- preset_val = Menu.yes()
- else:
- preset_val = Menu.no()
-
- prompt = _('Would you like to use swap on zram?')
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), preset_values=preset_val).run()
-
- match choice.type_:
- case MenuSelectionType.Skip: return preset
- case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True
diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py
deleted file mode 100644
index 7ee6fc07..00000000
--- a/archinstall/lib/user_interaction/utils.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from __future__ import annotations
-
-import getpass
-import signal
-import sys
-import time
-from typing import Any, Optional, TYPE_CHECKING
-
-from ..menu import Menu
-from ..models.password_strength import PasswordStrength
-from ..output import log
-
-if TYPE_CHECKING:
- _: Any
-
-# used for signal handler
-SIG_TRIGGER = None
-
-
-def get_password(prompt: str = '') -> Optional[str]:
- if not prompt:
- prompt = _("Enter a password: ")
-
- while password := getpass.getpass(prompt):
- if len(password.strip()) <= 0:
- break
-
- strength = PasswordStrength.strength(password)
- log(f'Password strength: {strength.value}', fg=strength.color())
-
- passwd_verification = getpass.getpass(prompt=_('And one more time for verification: '))
- if password != passwd_verification:
- log(' * Passwords did not match * ', fg='red')
- continue
-
- return password
-
- return None
-
-
-def do_countdown() -> bool:
- SIG_TRIGGER = False
-
- def kill_handler(sig: int, frame: Any) -> None:
- print()
- exit(0)
-
- def sig_handler(sig: int, frame: Any) -> None:
- global SIG_TRIGGER
- SIG_TRIGGER = True
- signal.signal(signal.SIGINT, kill_handler)
-
- original_sigint_handler = signal.getsignal(signal.SIGINT)
- signal.signal(signal.SIGINT, sig_handler)
-
- for i in range(5, 0, -1):
- print(f"{i}", end='')
-
- for x in range(4):
- sys.stdout.flush()
- time.sleep(0.25)
- print(".", end='')
-
- if SIG_TRIGGER:
- prompt = _('Do you really want to abort?')
- choice = Menu(prompt, Menu.yes_no(), skip=False).run()
- if choice.value == Menu.yes():
- exit(0)
-
- if SIG_TRIGGER is False:
- sys.stdin.read()
-
- SIG_TRIGGER = False
- signal.signal(signal.SIGINT, sig_handler)
-
- print()
- signal.signal(signal.SIGINT, original_sigint_handler)
-
- return True
diff --git a/archinstall/lib/utils/__init__.py b/archinstall/lib/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/archinstall/lib/utils/__init__.py
diff --git a/archinstall/lib/utils/singleton.py b/archinstall/lib/utils/singleton.py
new file mode 100644
index 00000000..55be70eb
--- /dev/null
+++ b/archinstall/lib/utils/singleton.py
@@ -0,0 +1,15 @@
+from typing import Dict, Any
+
+
+class _Singleton(type):
+ """ A metaclass that creates a Singleton base class when called. """
+ _instances: Dict[Any, Any] = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls._instances:
+ cls._instances[cls] = super().__call__(*args, **kwargs)
+ return cls._instances[cls]
+
+
+class Singleton(_Singleton('SingletonMeta', (object,), {})): # type: ignore
+ pass
diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py
new file mode 100644
index 00000000..2e42b3cf
--- /dev/null
+++ b/archinstall/lib/utils/util.py
@@ -0,0 +1,51 @@
+from pathlib import Path
+from typing import Any, TYPE_CHECKING, Optional, List
+
+from ..output import FormattedOutput
+from ..output import info
+
+if TYPE_CHECKING:
+ _: Any
+
+
+def prompt_dir(text: str, header: Optional[str] = None) -> Path:
+ if header:
+ print(header)
+
+ while True:
+ path = input(text).strip(' ')
+ dest_path = Path(path)
+ if dest_path.exists() and dest_path.is_dir():
+ return dest_path
+ info(_('Not a valid directory: {}').format(dest_path))
+
+
+def is_subpath(first: Path, second: Path):
+ """
+ Check if _first_ a subpath of _second_
+ """
+ try:
+ first.relative_to(second)
+ return True
+ except ValueError:
+ return False
+
+
+def format_cols(items: List[str], header: Optional[str] = None) -> str:
+ if header:
+ text = f'{header}:\n'
+ else:
+ text = ''
+
+ nr_items = len(items)
+ if nr_items <= 4:
+ col = 1
+ elif nr_items <= 8:
+ col = 2
+ elif nr_items <= 12:
+ col = 3
+ else:
+ col = 4
+
+ text += FormattedOutput.as_columns(items, col)
+ return text
diff --git a/archinstall/locales/README.md b/archinstall/locales/README.md
index e1266209..2e18f6a1 100644
--- a/archinstall/locales/README.md
+++ b/archinstall/locales/README.md
@@ -14,15 +14,15 @@ can be set with `setfont LatGrkCyr-8x16`
## Adding new languages
-New languages can be added simply by creating a new folder with the proper language abbreviation (see list `languages.json` if unsure).
+New languages can be added simply by creating a new folder with the proper language abbreviation (see list `languages.json` if unsure).
Run the following command to create a new template for a language
```
mkdir -p <abbr>/LC_MESSAGES/ && touch <abbr>/LC_MESSAGES/base.po
```
-After that run the script `./locales_generator.sh` it will automatically populate the new `base.po` file with the strings that
-need to be translated into the new language.
-For example the `base.po` might contain something like the following now
+After that run the script `./locales_generator.sh <lang_abbr>` it will automatically populate the new `base.po` file with the strings that
+need to be translated into the new language.
+For example the `base.po` might contain something like the following now
```
#: lib/user_interaction.py:82
msgid "Do you really want to abort?"
@@ -30,7 +30,9 @@ msgstr ""
```
The `msgid` is the identifier of the string in the code as well as the default text to be displayed, meaning that if no
-translation is provided for a language then this is the text that is going to be shown.
+translation is provided for a language then this is the text that is going to be shown.
+
+## Perform translations
To perform translations for a language this file can be edited manually or the neat `poedit` can be used (https://poedit.net/).
If editing the file manually, write the translation in the `msgstr` part
diff --git a/archinstall/locales/ar/LC_MESSAGES/base.mo b/archinstall/locales/ar/LC_MESSAGES/base.mo
index 6218cc97..33c12f10 100644
--- a/archinstall/locales/ar/LC_MESSAGES/base.mo
+++ b/archinstall/locales/ar/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po
index c9540b38..458dd77c 100644
--- a/archinstall/locales/ar/LC_MESSAGES/base.po
+++ b/archinstall/locales/ar/LC_MESSAGES/base.po
@@ -65,7 +65,7 @@ msgstr "اكتب حزمًا إضاÙية لتثبيتها (تÙÙصَل بالم
msgid "Copy ISO network configuration to installation"
msgstr "انسخ إعداد شبكة الـISO للتثبيت"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "استخدم Ù…Ùدير الشبكة (ضروري لإعداد الإنترنت باستخدام واجهة رسومية ÙÙŠ جنوم Ùˆ كيدي)"
msgid "Select one network interface to configure"
@@ -751,14 +751,13 @@ msgstr ""
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr ""
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
msgstr ""
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
@@ -792,6 +791,108 @@ msgstr ""
msgid "The font should be stored as {}"
msgstr ""
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "حدّÙد منطقة زمنية"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "حدّÙد واجهة شبكة واحدة للإعداد"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+msgid "Existing Partitions"
+msgstr ""
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "حدّÙد منطقة زمنية"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr ""
+
+msgid "Current profile selection"
+msgstr ""
+
+msgid "Remove all newly added partitions"
+msgstr ""
+
+msgid "Assign mountpoint"
+msgstr ""
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+msgid "Mark/Unmark as compressed"
+msgstr ""
+
+msgid "Set subvolumes"
+msgstr ""
+
+msgid "Delete partition"
+msgstr ""
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr ""
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+msgid "Total sectors: {}"
+msgstr ""
+
+msgid "Enter the start sector (default: {}): "
+msgstr ""
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr ""
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
msgid "Encryption type"
msgstr ""
@@ -810,17 +911,284 @@ msgstr ""
msgid "Select a FIDO2 device to use for HSM"
msgstr ""
-msgid "All settings will be reset, are you sure?"
+msgid "Use a best-effort default partition layout"
msgstr ""
-msgid "Back"
+msgid "Manual Partitioning"
+msgstr ""
+
+msgid "Pre-mounted configuration"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
+msgid "Configuration"
+msgstr ""
+
msgid "Password"
msgstr ""
-msgid "Partition encryption"
+msgid "All settings will be reset, are you sure?"
+msgstr ""
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+msgid "Installed packages"
+msgstr ""
+
+msgid "Add profile"
+msgstr ""
+
+msgid "Edit profile"
+msgstr ""
+
+msgid "Delete profile"
+msgstr ""
+
+msgid "Profile name: "
+msgstr ""
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "اكتب حزمًا إضاÙية لتثبيتها (تÙÙصَل بالمساÙات، اتركها Ùارغة للتخطي):"
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "اكتب حزمًا إضاÙية لتثبيتها (تÙÙصَل بالمساÙات، اتركها Ùارغة للتخطي):"
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+msgid "Disk configuration"
+msgstr ""
+
+msgid "Profiles"
+msgstr ""
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "حدّÙد واجهة شبكة واحدة للإعداد"
+
+msgid "Add a custom mirror"
+msgstr ""
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+msgid "Enter name (leave blank to skip): "
+msgstr ""
+
+msgid "Enter url (leave blank to skip): "
+msgstr ""
+
+msgid "Select signature check option"
+msgstr ""
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "حدّÙد منطقة زمنية"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+msgid "Save user configuration (including disk layout)"
+msgstr ""
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+msgid "Saving {} configuration files to {}"
+msgstr ""
+
+msgid "Mirrors"
+msgstr ""
+
+msgid "Mirror regions"
+msgstr ""
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "استخدم Ù…Ùدير الشبكة (ضروري لإعداد الإنترنت باستخدام واجهة رسومية ÙÙŠ جنوم Ùˆ كيدي)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+msgid "Enter start (default: sector {}): "
+msgstr ""
+
+msgid "Enter end (default: {}): "
+msgstr ""
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "هل تريد استخدام swap أو zram؟"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+msgid "Selected profiles: "
+msgstr ""
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Mark/Unmark as nodatacow"
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "هل تريد استخدام swap أو zram؟"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
msgstr ""
diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot
index 651fbd58..aee6c166 100644
--- a/archinstall/locales/base.pot
+++ b/archinstall/locales/base.pot
@@ -63,8 +63,8 @@ msgid "Copy ISO network configuration to installation"
msgstr ""
msgid ""
-"Use NetworkManager (necessary to configure internet graphically in GNOME and "
-"KDE)"
+"Use NetworkManager (necessary for configuring internet graphically in GNOME "
+"and KDE)"
msgstr ""
msgid "Select one network interface to configure"
@@ -793,16 +793,15 @@ msgid ""
"installation"
msgstr ""
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
msgid ""
-" - Maximum value : {max_downloads} ( Allows {max_downloads} parallel "
-"downloads, allows {max_downloads+1} downloads at a time )"
+" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads "
+"at a time )"
msgstr ""
msgid ""
@@ -844,6 +843,119 @@ msgstr ""
msgid "The font should be stored as {}"
msgstr ""
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+msgid "Select an execution mode"
+msgstr ""
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid ""
+"Profiles must have unique name, but profile definitions with duplicate name "
+"found: {}"
+msgstr ""
+
+msgid "Select one or more devices to use and configure"
+msgstr ""
+
+msgid ""
+"If you reset the device selection this will also reset the current disk "
+"layout. Are you sure?"
+msgstr ""
+
+msgid "Existing Partitions"
+msgstr ""
+
+msgid "Select a partitioning option"
+msgstr ""
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+msgid ""
+"This is a list of pre-programmed profiles_bck, they might make it easier to "
+"install things like desktop environments"
+msgstr ""
+
+msgid "Current profile selection"
+msgstr ""
+
+msgid "Remove all newly added partitions"
+msgstr ""
+
+msgid "Assign mountpoint"
+msgstr ""
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+msgid "Mark/Unmark as compressed"
+msgstr ""
+
+msgid "Set subvolumes"
+msgstr ""
+
+msgid "Delete partition"
+msgstr ""
+
+msgid "Partition"
+msgstr ""
+
+msgid ""
+"This partition is currently encrypted, to format it a filesystem has to be "
+"specified"
+msgstr ""
+
+msgid ""
+"Partition mount-points are relative to inside the installation, the boot "
+"would be /boot as an example."
+msgstr ""
+
+msgid ""
+"If mountpoint /boot is set, then the partition will also be marked as "
+"bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+msgid "Total sectors: {}"
+msgstr ""
+
+msgid "Enter the start sector (default: {}): "
+msgstr ""
+
+msgid ""
+"Enter the end sector of the partition (percentage or block number, default: "
+"{}): "
+msgstr ""
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
msgid "Encryption type"
msgstr ""
@@ -862,17 +974,305 @@ msgstr ""
msgid "Select a FIDO2 device to use for HSM"
msgstr ""
-msgid "All settings will be reset, are you sure?"
+msgid "Use a best-effort default partition layout"
msgstr ""
-msgid "Back"
+msgid "Manual Partitioning"
+msgstr ""
+
+msgid "Pre-mounted configuration"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
+msgid "Configuration"
+msgstr ""
+
msgid "Password"
msgstr ""
-msgid "Partition encryption"
+msgid "All settings will be reset, are you sure?"
+msgstr ""
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+msgid "Installed packages"
+msgstr ""
+
+msgid "Add profile"
+msgstr ""
+
+msgid "Edit profile"
+msgstr ""
+
+msgid "Delete profile"
+msgstr ""
+
+msgid "Profile name: "
+msgstr ""
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+msgid ""
+"Packages to be install with this profile (space separated, leave blank to "
+"skip): "
+msgstr ""
+
+msgid ""
+"Services to be enabled with this profile (space separated, leave blank to "
+"skip): "
+msgstr ""
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid ""
+"Sway needs access to your seat (collection of hardware devices i.e. "
+"keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+msgid "Disk configuration"
+msgstr ""
+
+msgid "Profiles"
+msgstr ""
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr ""
+
+msgid "Add a custom mirror"
+msgstr ""
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+msgid "Enter name (leave blank to skip): "
+msgstr ""
+
+msgid "Enter url (leave blank to skip): "
+msgstr ""
+
+msgid "Select signature check option"
+msgstr ""
+
+msgid "Select signature option"
+msgstr ""
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+msgid "Save user configuration (including disk layout)"
+msgstr ""
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion "
+"enabled)\n"
+"Save directory: "
+msgstr ""
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+msgid "Saving {} configuration files to {}"
+msgstr ""
+
+msgid "Mirrors"
+msgstr ""
+
+msgid "Mirror regions"
+msgstr ""
+
+msgid ""
+" - Maximum value : {} ( Allows {} parallel downloads, allows "
+"{max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr ""
+
+msgid ""
+"Use NetworkManager (necessary to configure internet graphically in GNOME and "
+"KDE)"
+msgstr ""
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+msgid "Enter start (default: sector {}): "
+msgstr ""
+
+msgid "Enter end (default: {}): "
+msgstr ""
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid ""
+"This option enables the number of parallel downloads that can occur during "
+"package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid ""
+" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid ""
+" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 "
+"download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid ""
+"Hyprland needs access to your seat (collection of hardware devices i.e. "
+"keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid ""
+"All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "Would you like to use unified kernel images?"
+msgstr ""
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid ""
+"Time syncronization not completing, while you wait - check the docs for "
+"workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid ""
+"Skipping waiting for automatic time sync (this can cause issues if time is "
+"out of sync during installation)"
+msgstr ""
+
+msgid ""
+"Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+msgid "Selected profiles: "
+msgstr ""
+
+msgid ""
+"Time synchronization not completing, while you wait - check the docs for "
+"workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Mark/Unmark as nodatacow"
+msgstr ""
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr ""
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
msgstr ""
diff --git a/archinstall/locales/cs/LC_MESSAGES/base.mo b/archinstall/locales/cs/LC_MESSAGES/base.mo
index a256081c..310e04f1 100644
--- a/archinstall/locales/cs/LC_MESSAGES/base.mo
+++ b/archinstall/locales/cs/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po
index a09c6001..05799df9 100644
--- a/archinstall/locales/cs/LC_MESSAGES/base.po
+++ b/archinstall/locales/cs/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.1\n"
+"X-Generator: Poedit 3.4.2\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] Soubor protokolu byl vytvořen zde: {} {}"
@@ -62,7 +62,7 @@ msgstr "Zadejte další balíÄky k instalaci (oddÄ›lené mezerou, ponechte prá
msgid "Copy ISO network configuration to installation"
msgstr "Zkopírovat do instalace konfiguraci sítě z ISO"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Použít NetworkManager (potřebné pro grafickou konfiguraci v GNOME a KDE)"
msgid "Select one network interface to configure"
@@ -95,10 +95,10 @@ msgid "Enter a desired filesystem type for the partition"
msgstr "Zadejte požadovaný souborový systém pro oddíl"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Zadejte poÄáteÄní pozici (jednotky: s, GB, %, atd. ; výchozí: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Zadejte koncovou pozici (jednotky: s, GB, %, atd. ; napÅ™.: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} obsahuje oddíly ve frontě, toto je odstraní, jste si jisti?"
@@ -205,7 +205,6 @@ msgid ""
"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
"\n"
-"\n"
"Zvolte ovladaÄ grafické karty nebo ponechte prázdné k instalaci vÅ¡ech open-source ovladaÄů"
msgid "All open-source (default)"
@@ -664,7 +663,7 @@ msgid "Press Enter to continue."
msgstr "StisknÄ›te ENTER pro pokraÄování."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "PÅ™ejete si vlézt skrze chroot do své novÄ› vytvoÅ™ené instalace a provést závÄ›reÄnou konfiguraci?"
+msgstr "PÅ™ejete si vstoupit skrze chroot do své novÄ› vytvoÅ™ené instalace a provést závÄ›reÄnou konfiguraci?"
msgid "Are you sure you want to reset this setting?"
msgstr "SkuteÄnÄ› si pÅ™ejete resetovat toto nastavení?"
@@ -786,18 +785,17 @@ msgstr "Nakonfigurováno {} rozhraní"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Tato možnost povolí specifikovaný poÄet paralelních stahování, která mohou nastat pÅ™i instalaci"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Zadejte povolený poÄet paralelních stahování.\n"
-" (Zadejte hodnotu mezi 1 a {max_downloads})\n"
+" (Zadejte hodnotu mezi 1 a {})\n"
"Poznámka:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Maximální hodnota : {max_downloads} (Povolí {max_downloads} paralelních stahování, povolí {max_downloads+1} stahování naráz )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Maximální hodnota : {} (Povolí {} paralelních stahování, povolí {} stahování naráz )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Minimální hodnota : 1 (Povolí 1 paralelní stahování, povolí 2 stahování naráz)"
@@ -812,64 +810,432 @@ msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {ma
msgid "Parallel Downloads"
msgstr "Paralelní stahování"
-#, fuzzy
msgid "ESC to skip"
msgstr "Pomocí ESC pÅ™eskoÄíte"
msgid "CTRL+C to reset"
-msgstr ""
+msgstr "CTRL+C pro zrušení"
msgid "TAB to select"
-msgstr ""
+msgstr "TAB k výběru"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[Výchozí hodnota: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Abyste mohli používat tento překlad, nainstalujte prosím písmo, které zvolený jazyk podporuje."
msgid "The font should be stored as {}"
-msgstr ""
+msgstr "Písmo by mělo být uložené jako {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall vyžaduje ke spuštění oprávnění správce (root). Použijte --help pro více informací."
+
+msgid "Select an execution mode"
+msgstr "Zvolte způsob provedení"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Nepodařilo se získat profil ze zadané url: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Profily musí mít unikátní jméno. Následující duplicity byly nalezeny: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Zvolte jeden nebo více pevných disků k použití a konfiguraci"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Pokud zrušíte výbÄ›r disku, také tím zrušíte stávající rozdÄ›lení. PÅ™ejete si pokraÄovat?"
+
+msgid "Existing Partitions"
+msgstr "Existující oddíly"
+
+msgid "Select a partitioning option"
+msgstr "Zvolte možnosti rozdělení disku"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Zadejte kořenový adresář připojených zařízení: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Minimální kapacita pro oddíl /home: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Minimální kapacita pro oddíl s Arch Linux: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Toto je seznam před-programovaných profilů, které by mohly usnadnit instalaci věcí jako jsou desktopová prostředí"
+
+msgid "Current profile selection"
+msgstr "Aktuální výběr profilu"
+
+msgid "Remove all newly added partitions"
+msgstr "Smazat všechny nově přidané oddíly"
+
+msgid "Assign mountpoint"
+msgstr "PÅ™iÅ™aÄte přípojný bod"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "OznaÄení/OdznaÄení oddílu ke zformátování (vymaže data)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "OznaÄit/OdznaÄit jako zavádÄ›cí"
+
+msgid "Change filesystem"
+msgstr "Změnit souborový systém"
+
+msgid "Mark/Unmark as compressed"
+msgstr "OznaÄit/OdznaÄit oddíl s kompresí"
+
+msgid "Set subvolumes"
+msgstr "Nastavit podsvazky"
+
+msgid "Delete partition"
+msgstr "Smazat oddíl"
+
+msgid "Partition"
+msgstr "Diskový oddíl"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Tento oddíl je zašifrovaný, k naformátování zadejte souborový systém"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Přípojné body diskových oddílů jsou relativní uvnitř instalace, například spouštěcí bod by byl /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Pokud je nastaven přípojný bod /boot, oddíl je zároveň oznaÄený jako zavádÄ›cí."
+
+msgid "Mountpoint: "
+msgstr "Přípojný bod: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Momentálně volné sektory na zařízení {}:"
+
+msgid "Total sectors: {}"
+msgstr "Celkem sektorů: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Zadejte poÄáteÄní sektor (výchozí: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Zadejte koncový sektor oddílu (procenta nebo Äíslo bloku, výchozí: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Tímto smažete vÅ¡echny novÄ› vytvoÅ™ené oddíly, pÅ™ejete si pokraÄovat?"
+
+msgid "Partition management: {}"
+msgstr "Správa diskových oddílů: {}"
+
+msgid "Total length: {}"
+msgstr "Celková délka: {}"
-#, fuzzy
msgid "Encryption type"
-msgstr "Šifrovací heslo"
+msgstr "Typ šifrování"
msgid "Partitions"
-msgstr ""
+msgstr "Diskové oddíly"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Žádné HSM zařízení není dostupné"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Zvolte oddíl, který bude oznaÄen jako Å¡ifrovaný"
+msgstr "Zvolte oddíl k zašifrování"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "Zvolte způsob šifrování disku"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Vyberte FIDO2 zařízení abyste mohli používat HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Použít chytré výchozí rozdělení oddílů"
+
+msgid "Manual Partitioning"
+msgstr "RuÄní rozdÄ›lení disku"
+
+msgid "Pre-mounted configuration"
+msgstr "Pre-mounted konfigurace"
+
+msgid "Unknown"
+msgstr "Neznámý"
+
+msgid "Partition encryption"
+msgstr "Šifrování diskového oddílu"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formátuji {} na "
+
+msgid "↠Back"
+msgstr "↠Zpět"
+
+msgid "Disk encryption"
+msgstr "Šifrování disku"
+
+msgid "Configuration"
+msgstr "Konfigurace"
+
+msgid "Password"
+msgstr "Heslo"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} obsahuje oddíly ve frontě, toto je odstraní, jste si jisti?"
+msgstr "Všechny volby budou zrušeny, jste si jisti?"
msgid "Back"
+msgstr "Zpět"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Prosím vyberte přihlašovací obrazovku k instalaci pro vybraný profil: {}"
+
+msgid "Environment type: {}"
+msgstr "Typ prostředí: {}"
+
+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 "Sway nepodporuje proprietární ovladaÄ Nvidia. PravdÄ›podobnÄ› narazíte na problémy, pÅ™ejete si pokraÄovat?"
+
+msgid "Installed packages"
+msgstr "Instalované balíÄky"
+
+msgid "Add profile"
+msgstr "Přidat profil"
+
+msgid "Edit profile"
+msgstr "Změnit profil"
+
+msgid "Delete profile"
+msgstr "Smazat profil"
+
+msgid "Profile name: "
+msgstr "Jméno profilu: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Zadané jméno profilu již existuje. Zkuste to znovu"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Zadejte další balíÄky k instalaci v tomto profilu (oddÄ›lené mezerou, ponechte prázdné k pÅ™eskoÄení): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Zadejte povolené služby v tomto profilu (oddÄ›lené mezerou, ponechte prázdné k pÅ™eskoÄení): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Má se tento profil použít pro instalaci?"
+
+msgid "Create your own"
+msgstr "Vytvořit vlastní"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"Zvolte ovladaÄ grafické karty nebo ponechte prázdné k instalaci vÅ¡ech open-source ovladaÄů"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway vyžaduje přístup k vašemu sezení (soubor zařízení jako např. klávesnice, myš, atd.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Vyberte možnost jak zpřístupnit pro Sway váš hardware"
-#, fuzzy
-msgid "Password"
-msgstr "Heslo správce (root)"
+msgid "Graphics driver"
+msgstr "Grafický ovladaÄ"
-msgid "Partition encryption"
+msgid "Greeter"
+msgstr "Přihlašovací obrazovka"
+
+msgid "Please chose which greeter to install"
+msgstr "Prosím vyberte přihlašovací obrazovku k instalaci"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Toto je seznam před-programovaných výchozích profilů"
+
+msgid "Disk configuration"
+msgstr "Konfigurace disku"
+
+msgid "Profiles"
+msgstr "Profily"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Hledám možné adresáře k uložení konfiguraÄních souborů ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Zvolte jeden nebo více adresářů k uložení konfiguraÄních souborů"
+
+msgid "Add a custom mirror"
+msgstr "Přidat vlastní zrcadlo"
+
+msgid "Change custom mirror"
+msgstr "Změnit vlastní zrcadla"
+
+msgid "Delete custom mirror"
+msgstr "Smazat vlastní zrcadla"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Zadejte jméno (ponechte prázdné k pÅ™eskoÄení): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Zadejte url (ponechte prázdné k pÅ™eskoÄení): "
+
+msgid "Select signature check option"
+msgstr "Zvolit možnost kontroly podpisu"
+
+msgid "Select signature option"
+msgstr "Zvolit možnost podpisu"
+
+msgid "Custom mirrors"
+msgstr "Vlastní zrcadla"
+
+msgid "Defined"
+msgstr "Definováno"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Uložit uživatelskou konfiguraci (vÄetnÄ› rozdÄ›lení disků)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Zadejte adresář pro uložení konfigurace (konfigurací). Doplňování pomocí tab je zapnuto\n"
+"Adresář pro uložení: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Chcete uložit {} konfiguraÄních souborů do následujícího umístÄ›ní?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Ukládám {} konfiguraÄních souborů do {}"
+
+msgid "Mirrors"
+msgstr "Zrcadla"
+
+msgid "Mirror regions"
+msgstr "Oblasti zrcadel"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Maximální hodnota : {} (Povolí {} paralelních stahování, povolí {max_downloads+1} stahování naráz )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {}, nebo 0 pro vypnutí]"
+
+msgid "Locales"
+msgstr "Lokalizace"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Použít NetworkManager (potřebné pro grafickou konfiguraci v GNOME a KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Celkem: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Všechny zadané hodnoty mohou byt doplněny jednotkou: B, KB, KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Pokud nejsou zadány jednotky, hodnoty jsou brány jako sektory"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Zadejte poÄátek (výchozí: sektor {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Zadejte konec (výchozí: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Nelze rozpoznat FIDO2 zařízení. Je nainstalován balíÄek libfido2?"
+
+msgid "Path"
+msgstr "Cesta"
+
+msgid "Manufacturer"
+msgstr "Výrobce"
+
+msgid "Product"
+msgstr "Produkt"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Chybná konfigurace: {error}"
+
+msgid "Type"
+msgstr "Typ"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Tato možnost povolí specifikovaný poÄet paralelních stahování, která mohou nastat pÅ™i instalaci"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Zadejte povolený poÄet paralelních stahování.\n"
+"\n"
+"Poznámka:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Maximální doporuÄená hodnota : {} (Povolí {} paralelních stahování naráz )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Zakázáno/Výchozí : 0 (Zakáže paralelní stahování, povolí pouze 1 stahování naráz)\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [nebo 0 pro vypnutí]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland vyžaduje přístup k vašemu sezení (soubor zařízení jako např. klávesnice, myš, atd.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Vyberte možnost jak zpřístupnit pro Hyprland váš hardware"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Všechny zadané hodnoty mohou byt doplněny jednotkou: %, B, KB, KiB, MB, MiB..."
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Zadejte poÄáteÄní sektor (procenta nebo Äíslo bloku, výchozí: {}): "
+msgid "Would you like to use unified kernel images?"
+msgstr "Přejete si použít Unified Kernel Images?"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Zadejte koncový sektor oddílu (procenta nebo Äíslo bloku, napÅ™. {}): "
+msgid "Unified kernel images"
+msgstr "Unified Kernel Images"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "ÄŒekám na dokonÄení synchronizace Äasu (timedatectl show)."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Synchronizace Äasu není dokonÄena, mezitím se můžete podívat na možnosti Å™eÅ¡ení: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "PÅ™eskakuji Äekání na automatickou synchronizaci Äasu (nesynchronizovaný Äas může způsobit problémy bÄ›hem instalace)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "ÄŒekám na dokonÄení synchronizace Arch Linux keyring (archlinux-keyring-wkd-sync)."
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Smazat profil"
+
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Synchronizace Äasu není dokonÄena, mezitím se můžete podívat na možnosti Å™eÅ¡ení: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "OznaÄit/OdznaÄit jako zavádÄ›cí"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Přejete si použít BTRFS kompresi?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo
index 0572f28b..7dda3e36 100644
--- a/archinstall/locales/de/LC_MESSAGES/base.mo
+++ b/archinstall/locales/de/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po
index ac677188..61615628 100644
--- a/archinstall/locales/de/LC_MESSAGES/base.po
+++ b/archinstall/locales/de/LC_MESSAGES/base.po
@@ -9,76 +9,76 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.0.1\n"
+"X-Generator: Poedit 3.3.2\n"
msgid "[!] A log file has been created here: {} {}"
-msgstr "[!] Eine Logdatei wurde erstellt: {} {}"
+msgstr "[!] Eine Logdatei wurde hier erstellt: {} {}"
msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
-msgstr "Bitte melden sie das Problem mit der erstellten Datei auf https://github.com/archlinux/archinstall/issues"
+msgstr " Bitte melden Sie das Problem mit der erstellten Datei auf https://github.com/archlinux/archinstall/issues"
msgid "Do you really want to abort?"
msgstr "Wollen Sie wirklich abbrechen?"
msgid "And one more time for verification: "
-msgstr "Und nocheinmal zur Besätigung: "
+msgstr "Und noch einmal zur Bestätigung: "
msgid "Would you like to use swap on zram?"
-msgstr "Möchten Sie swap mit zram verwenden?"
+msgstr "Möchten Sie Auslagerungsspeicher (Swap) als zram verwenden?"
msgid "Desired hostname for the installation: "
-msgstr "Gewnüschter Hostname für die Installation: "
+msgstr "Gewünschter Gerätename/Hostname für die Installation: "
msgid "Username for required superuser with sudo privileges: "
-msgstr "Benutzername für den erforderlichen superuser mit sudo Rechten: "
+msgstr "Benutzername für den erforderlichen Superuser mit sudo Rechten: "
msgid "Any additional users to install (leave blank for no users): "
-msgstr "Geben Sie weitere Benutzernamen ein die installiert werden sollen (leer lassen für keine weiteren Benutzer): "
+msgstr "Geben Sie weitere Benutzernamen ein, die installiert werden sollen (leer lassen für keine weiteren Benutzer): "
msgid "Should this user be a superuser (sudoer)?"
-msgstr "Soll dieser Benutzer ein superuser sein (sudoer)?"
+msgstr "Soll dieser Benutzer ein Superuser sein (sudoer)?"
msgid "Select a timezone"
msgstr "Bitte wählen Sie eine Zeitzone aus"
msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
-msgstr "Möchten Sie GRUB als bootloader anstelle von system-boot verwenden?"
+msgstr "Möchten Sie GRUB als Bootloader anstelle von systemd-boot verwenden?"
msgid "Choose a bootloader"
-msgstr "Bitte wählen Sie einen bootloader aus"
+msgstr "Bitte wählen Sie einen Bootloader aus"
msgid "Choose an audio server"
-msgstr "Bitte wählen Sie einen Audio server aus"
+msgstr "Bitte wählen Sie einen Audioserver aus"
msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
-msgstr "Nur die Packete base, base-devel, linux, linux-firmware, efibootmgr und optionale Profilpackete werden installiert"
+msgstr "Nur Pakete wie base, base-devel, linux, linux-firmware, efibootmgr und optionale Profilpakete werden installiert."
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "Wenn Sie einen Webbrowser, z.B. Firefox oder Chromium, installieren möchten, können Sie diese nun eingeben."
+msgstr "Wenn Sie einen Webbrowser installieren möchten, wie z.B. Firefox oder Chromium, können Sie diese nun eingeben."
msgid "Write additional packages to install (space separated, leave blank to skip): "
-msgstr "Schreiben Sie zusätzliche Packete die installiert werden sollen mit einem Leerzeichen getrennt (zum Überspringen leer lassen): "
+msgstr "Schreiben Sie zusätzliche Pakete die installiert werden sollen (mit einem Leerzeichen getrennt, zum Überspringen leer lassen): "
msgid "Copy ISO network configuration to installation"
-msgstr "ISO netzwerk Einstellungen in die Installation kopieren"
+msgstr "ISO-Netzwerk Einstellungen in die Installation kopieren"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
-msgstr "NetworkManager benutzen (notwendig um Internet auf graphische Weise in GNOME und KDE einzustellen)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "NetworkManager nutzen (notwendig um Internetverbindungen grafisch in GNOME und KDE einzustellen)"
msgid "Select one network interface to configure"
-msgstr "Bitte wählen Sie ein netzwerk zur Konfiguration aus"
+msgstr "Bitte wählen Sie einen Netzwerkadapter zur Konfiguration aus"
msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
-msgstr "Bitte wählen Sie einen Modus zur Konfiguration von \"{}\" aus oder Überspringen um mit dem voreingestellten Modus \"{}\" fortzufahren"
+msgstr "Bitte wählen Sie einen Modus zur Konfiguration von \"{}\" aus oder überspringen, um mit dem voreingestellten Modus \"{}\" fortzufahren"
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
-msgstr "Bitte geben Sie eine IP Adresse und ein Subnet für {} ein (z.B. 192.168.0.5/24)"
+msgstr "Bitte geben Sie die IP-Adresse mit Subnetzgröße für {} ein (z.B. 192.168.0.5/24): "
msgid "Enter your gateway (router) IP address or leave blank for none: "
-msgstr "Bitte geben Sie eine gateway (router) IP Adresse ein (leer lassen für kein Adresse): "
+msgstr "Bitte geben Sie das Gateway (Router) IP-Adresse ein (leer lassen für keines): "
msgid "Enter your DNS servers (space separated, blank for none): "
-msgstr "Bitte geben Sie die DNS server ein (mit Leerzeichen getrennt oder leer lassen für keinen server): "
+msgstr "Bitte geben Sie die DNS-Server ein (mit Leerzeichen getrennt oder leer lassen für keinen Server): "
msgid "Select which filesystem your main partition should use"
msgstr "Bitte wählen Sie ein Dateisystem aus, welches für die Hauptpartition verwendet werden soll"
@@ -94,7 +94,7 @@ msgstr ""
"{}"
msgid "Enter a desired filesystem type for the partition"
-msgstr "Bitte wählen Sie einen Dateisystemtyp für die Partition aus"
+msgstr "Bitte wählen Sie den gewünschten Dateisystemtyp für die Partition aus"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
msgstr "Bitte geben Sie die Startposition ein (in parted Einheiten: s, GB, %, etc. ; default: {}): "
@@ -103,7 +103,7 @@ msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
msgstr "Bitte geben Sie die Endposition ein (in parted Einheiten: s, GB, %, etc. ; default: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
-msgstr "{} enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?"
+msgstr "{} enthält Partitionen in der Warteschlange, diese werden damit entfernt, sind Sie sicher?"
msgid ""
"{}\n"
@@ -112,7 +112,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Wählen sie anhand vom index welche Partitionen gelöscht werden sollen"
+"Wählen Sie anhand vom Index aus, welche Partitionen gelöscht werden sollen"
msgid ""
"{}\n"
@@ -121,13 +121,13 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Wählen sie anhand vom index welche Partitionen zu mounten"
+"Wählen Sie anhand vom Index aus, welche Partitionen wo eingehängt werden sollen"
msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
-msgstr " * Die Mountorte sind relativ zur Installation, zum Beispiel boot würde gemountet auf /boot"
+msgstr " * Die Einhängeorte sind relativ zur Installation, boot würde beispielsweise /boot entsprechen."
msgid "Select where to mount partition (leave blank to remove mountpoint): "
-msgstr "Bitte geben sie an wo die Partition gemounted werden soll (leer lassen um den Mountort zu entfernen): "
+msgstr "Bitte geben Sie an wo die Partition eingehängt werden soll (leer lassen um den Einhängeort zu entfernen): "
msgid ""
"{}\n"
@@ -136,7 +136,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Bitte wählen sie welche Partition formatiert werden soll"
+"Bitte wählen Sie aus, welche Partition formatiert werden soll"
msgid ""
"{}\n"
@@ -145,7 +145,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Bitte wählen sie welche Partition verschlüsselt werden soll"
+"Bitte wählen Sie aus, welche Partition verschlüsselt werden soll"
msgid ""
"{}\n"
@@ -154,7 +154,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Bitte wählen sie welche Partition bootbar ist"
+"Bitte wählen Sie aus, welche Partition bootfähig markiert werden soll"
msgid ""
"{}\n"
@@ -163,43 +163,43 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Bitte wählen sie auf welche Partition ein Dateisystem eingerichtet werden soll"
+"Bitte wählen Sie aus, auf welcher Partition ein Dateisystem erstelt werden soll"
msgid "Enter a desired filesystem type for the partition: "
-msgstr "Bitte geben sie einen gewünschten Dateisystemtyp für die Partition ein: "
+msgstr "Bitte geben Sie einen gewünschten Dateisystemtyp für die Partition ein: "
msgid "Archinstall language"
msgstr "Sprache für Archinstall"
msgid "Wipe all selected drives and use a best-effort default partition layout"
-msgstr "Alle Laufwerke löschen und ein vorgegebenes Partitionenlayout verwenden"
+msgstr "Alle Laufwerke löschen und ein empfohlenes Partitionslayout verwenden"
msgid "Select what to do with each individual drive (followed by partition usage)"
-msgstr "Bitte geben sie an was mit jedem individuellem Laufwerk geschehen soll"
+msgstr "Bitte geben Sie individuell an, was mit jedem Laufwerk geschehen soll"
msgid "Select what you wish to do with the selected block devices"
-msgstr "Bitte wählen sie was mit dem ausgewählten Gerät geschehen soll"
+msgstr "Bitte wählen Sie was mit den ausgewählten Geräten machen wollen"
msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
-msgstr "Dies ist eine Liste von bereits programmierten Profilen, diese ermöglichen es einfacher Desktop Umgebungen einzustellen"
+msgstr "Dies ist eine Auflistung vorprogrammierter Profile, die es einfacher ermöglichen, Dinge wie Desktop-Umgebungen zu installieren"
msgid "Select keyboard layout"
-msgstr "Bitte wählen sie ein Tastaturlayout aus"
+msgstr "Bitte wählen Sie ein Tastaturlayout aus"
msgid "Select one of the regions to download packages from"
-msgstr "Bitte wählen sie eine Region zum downloaden von Packeten aus"
+msgstr "Bitte wählen Sie eine Region zum herunterladen von Paketen aus"
msgid "Select one or more hard drives to use and configure"
-msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen"
+msgstr "Bitte wählen Sie ein oder mehrere Laufwerk(e) aus, die konfiguriert und verwendet werden sollen"
msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "Für die beste kompabilität mit ihrer AMD hardware, sollten sie womöglich die open-source oder AMD / ATI optionen verwenden"
+msgstr "Für die beste Kompatibilität mit ihrer AMD Hardware, sollten Sie womöglich die quelloffenen oder andernfalls AMD/ATI-Optionen verwenden."
msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
-msgstr "Für die beste kompabilität mit ihrer Intel hardware, sollten sie womöglich die open-source oder Intel optionen verwenden.\n"
+msgstr "Für die beste Kompatibilität mit ihrer Intel Hardware, sollten Sie womöglich die quelloffenen oder andernfalls Intel-Optionen verwenden.\n"
msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
-msgstr "Für die beste kompabilität mit ihrer Nvidia hardware, sollten sie womöglich die Nvidia proprietary driver option verwenden.\n"
+msgstr "Für die beste Kompatibilität mit ihrer Nvidia-Hardware, sollten Sie den proprietären Nvidia-Treiber verwenden.\n"
msgid ""
"\n"
@@ -208,43 +208,43 @@ msgid ""
msgstr ""
"\n"
"\n"
-"Bitte wählen sie einen Grafiktreiber aus oder leer lassen um alle open-source Treiber zu installieren"
+"Bitte wählen Sie einen Grafikkartentreiber aus oder leer lassen, um alle quelloffenen Treiber zu installieren"
msgid "All open-source (default)"
-msgstr "Alle open-source (default)"
+msgstr "Alle Quelloffene (Standard)"
msgid "Choose which kernels to use or leave blank for default \"{}\""
-msgstr "Bitte wählen sie welche Kernel benutzt werden sollen oder leer lassen für default \"{}\""
+msgstr "Bitte wählen Sie welche Kernel benutzt werden sollen oder leer lassen für Standard \"{}\""
msgid "Choose which locale language to use"
-msgstr "Bitte wählen sie eine lokale Sprache aus"
+msgstr "Bitte wählen Sie aus, welche lokale Sprache verwendet werden soll"
msgid "Choose which locale encoding to use"
-msgstr "Bitte wählen sie eine lokale Kodierung aus"
+msgstr "Bitte wählen Sie aus, welche lokale Kodierung verwendet werden soll"
msgid "Select one of the values shown below: "
-msgstr "Bitte wählen sie einen der folgenden Werte aus:"
+msgstr "Bitte wählen Sie einen der folgenden Werte aus: "
msgid "Select one or more of the options below: "
-msgstr "Bitte wählen sie eine oder mehrere Optionen aus: "
+msgstr "Bitte wählen Sie eine oder mehrere Option(en) aus: "
msgid "Adding partition...."
-msgstr "Partitionen werden hinzugefügt..."
+msgstr "Partition wird hinzugefügt..."
msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
-msgstr "Bitte geben sie einen gültigen Dateisystemtyp ein um fortzufahren. Wenden sie sich an \"man parted\" für eine Liste von gültigen Typen."
+msgstr "Bitte geben Sie einen gültigen Dateisystemtyp ein um fortzufahren. Verwenden Sie `man parted` um eine Liste gültiger Typen einzusehen."
msgid "Error: Listing profiles on URL \"{}\" resulted in:"
-msgstr "Fehler: Auflistung von Profilen mit URL \"{}\":"
+msgstr "Fehler: Auflistung von Profilen mit URL \"{}\" ergab:"
msgid "Error: Could not decode \"{}\" result as JSON:"
-msgstr "Fehler: \"{}\" konnte nicht in ein JSON format dekodiert werden:"
+msgstr "Fehler: \"{}\" konnte nicht zu JSON dekodiert werden:"
msgid "Keyboard layout"
msgstr "Tastaturlayout"
msgid "Mirror region"
-msgstr "Mirror-region"
+msgstr "Spiegelserver-Region"
msgid "Locale language"
msgstr "Lokale Sprache"
@@ -253,15 +253,13 @@ msgid "Locale encoding"
msgstr "Lokale Kodierung"
msgid "Drive(s)"
-msgstr "Laufwerke"
+msgstr "Laufwerk(e)"
-#, fuzzy
msgid "Disk layout"
-msgstr "Laufwerke-layout speichern"
+msgstr "Laufwerkslayout"
-#, fuzzy
msgid "Encryption password"
-msgstr "Verschlüsselungspasswort angeben"
+msgstr "Verschlüsselungspasswort"
msgid "Swap"
msgstr "Swap"
@@ -269,36 +267,35 @@ msgstr "Swap"
msgid "Bootloader"
msgstr "Bootloader"
-#, fuzzy
msgid "Root password"
-msgstr "Root Passwort"
+msgstr "Root-Passwort"
msgid "Superuser account"
-msgstr "Superuser Konto"
+msgstr "Superuser-Konto"
msgid "User account"
msgstr "Benutzerkonto"
msgid "Profile"
-msgstr "Profile"
+msgstr "Profil"
msgid "Audio"
msgstr "Audio"
msgid "Kernels"
-msgstr "Kernels"
+msgstr "Kernel"
msgid "Additional packages"
-msgstr "Zus. Packete"
+msgstr "Zusätzliche Pakete"
msgid "Network configuration"
-msgstr "Netzwerkonfiguration"
+msgstr "Netzwerkkonfiguration"
msgid "Automatic time sync (NTP)"
msgstr "Autom. Zeitsynchronisierung (NTP)"
msgid "Install ({} config(s) missing)"
-msgstr "Installieren ({} konfiguration(en) ausständig)"
+msgstr "Installieren ({} Konfiguration(en) ausständig)"
msgid ""
"You decided to skip harddrive selection\n"
@@ -306,10 +303,10 @@ msgid ""
"WARNING: Archinstall won't check the suitability of this setup\n"
"Do you wish to continue?"
msgstr ""
-"Sie haben sich entschieden keine Laufwerke auszuwählen\n"
-"und jene Einstellungen zu verwenden welche momentan auf {} verfügbar sind (experimentell)\n"
-"WARNUNG: Archinstall wird die Kompabilität der Einstellung nicht überprüfen\n"
-"Wollen sie trotzdem fortfahren?"
+"Sie haben sich entschieden, kein Laufwerk auszuwählen\n"
+"und somit werden Laufwerkseinstellungen verwendet, welche am Einhängeort {} vorgefunden werden (experimentell)\n"
+"WARNUNG: Archinstall wird die Kompatibilität der Einstellung nicht überprüfen\n"
+"Wollen Sie trotzdem fortfahren?"
msgid "Re-using partition instance: {}"
msgstr "Wiederverwenden der Partitionsinstanz: {}"
@@ -321,19 +318,19 @@ msgid "Delete a partition"
msgstr "Partition löschen"
msgid "Clear/Delete all partitions"
-msgstr "Alle partitionen löschen"
+msgstr "Alle Partitionen löschen"
msgid "Assign mount-point for a partition"
-msgstr "Mountort für Partition angeben"
+msgstr "Einhängeort für Partition angeben"
msgid "Mark/Unmark a partition to be formatted (wipes data)"
-msgstr "Markieren welche Partition formattiert werden soll (alle Daten werden gelöscht)"
+msgstr "Markieren, ob eine Partition formatiert werden soll (alle Daten werden gelöscht)"
msgid "Mark/Unmark a partition as encrypted"
-msgstr "Markieren welche Partitionen verschlüsselt werden sollen"
+msgstr "Markieren, ob eine Partition verschlüsselt werden soll"
msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
-msgstr "Markieren welche Partition bootbar ist (automatisch für /boot)"
+msgstr "Markieren, ob eine Partition als bootfähig sein soll (automatisch für /boot)"
msgid "Set desired filesystem for a partition"
msgstr "Bitte wählen Sie einen Dateisystemtyp für die Partition aus"
@@ -342,16 +339,16 @@ msgid "Abort"
msgstr "Abbrechen"
msgid "Hostname"
-msgstr "Hostnamen"
+msgstr "Gerätename/Hostname"
msgid "Not configured, unavailable unless setup manually"
-msgstr "Nicht konfiguriert, unverfügbar wenn nicht selber eingestellt"
+msgstr "Nicht konfiguriert, nicht verfügbar wenn nicht selber eingestellt"
msgid "Timezone"
msgstr "Zeitzone"
msgid "Set/Modify the below options"
-msgstr "Setzen sie die unten stehenden Einstellungen"
+msgstr "Setzen/Modifizieren Sie die unten stehenden Einstellungen"
msgid "Install"
msgstr "Installieren"
@@ -360,7 +357,7 @@ msgid ""
"Use ESC to skip\n"
"\n"
msgstr ""
-"ESC um zu Ãœberspringen\n"
+"ESC um zu überspringen\n"
"\n"
msgid "Suggest partition layout"
@@ -373,42 +370,42 @@ msgid "Enter a encryption password for {}"
msgstr "Verschlüsselungspasswort angeben für {}"
msgid "Enter disk encryption password (leave blank for no encryption): "
-msgstr "Geben sie ein Verschlüsselungspasswort ein (leer lassen um zu Überspringen): "
+msgstr "Geben Sie ein Verschlüsselungspasswort ein (leer lassen um Verschlüsselung zu deaktivieren): "
msgid "Create a required super-user with sudo privileges: "
-msgstr "Geben sie einen super-user mit sudo Privilegien an: "
+msgstr "Geben Sie einen Superuser mit sudo Privilegien an: "
msgid "Enter root password (leave blank to disable root): "
-msgstr "Geben sie ein Root passwort ein (leer lassen um Root zu deaktivieren): "
+msgstr "Geben Sie ein Root-Passwort ein (leer lassen um Root zu deaktivieren): "
msgid "Password for user \"{}\": "
msgstr "Passwort für Benutzer \"{}\": "
msgid "Verifying that additional packages exist (this might take a few seconds)"
-msgstr "Angegebene Packete werden verifiziert (dies könnte einige Sekunden dauern)"
+msgstr "Angegebene Pakete werden verifiziert (dies könnte einige Sekunden dauern)"
msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
-msgstr "Möchten sie automatische Zeitsynchronisierung mit dem default Server einschalten?\n"
+msgstr "Möchten Sie die automatische Zeitsynchronisierung (NTP) mit dem Standard Server verwenden?\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 ""
-"Hardware Zeit und andere Einstellungsschritte könnten notwendig sein um NTP zu benutzen.\n"
-"Für weitere Informationen wenden sie sich bitte an das Arch wiki"
+"Hardwarezeit und andere Einstellungsschritte könnten notwendig sein um NTP zu benutzen.\n"
+"Für weitere Informationen wenden Sie sich bitte an das Arch wiki"
msgid "Enter a username to create an additional user (leave blank to skip): "
-msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Ãœberspringen): "
+msgstr "Geben Sie einen Benutzernamen ein, um diesen zusätzlich anzulegen (leer lassen zum Überspringen): "
msgid "Use ESC to skip\n"
-msgstr "ESC um zu Ãœberspringen\n"
+msgstr "ESC um zu überspringen\n"
msgid ""
"\n"
" Choose an object from the list, and select one of the available actions for it to execute"
msgstr ""
"\n"
-" Wählen sie ein Object aus der Liste aus und wählen sie anschließend eine Aktion dafür aus "
+" Wählen Sie ein Objekt aus der Liste aus und wählen Sie anschließend eine Aktion zum ausführen dafür aus"
msgid "Cancel"
msgstr "Abbrechen"
@@ -429,10 +426,10 @@ msgid "Delete"
msgstr "Löschen"
msgid "Select an action for '{}'"
-msgstr "Wählen sie eine Aktion aus für '{}'"
+msgstr "Wählen Sie eine Aktion aus für '{}'"
msgid "Copy to new key:"
-msgstr "Kopieren nach neuem Schlüssel:"
+msgstr "Kopieren zum neuen Schlüssel:"
msgid "Unknown nic type: {}. Possible values are {}"
msgstr "Nicht erkannter Netzwerinterfacecontroller: {}. Erlaubte Werte {}"
@@ -442,16 +439,16 @@ msgid ""
"This is your chosen configuration:"
msgstr ""
"\n"
-"Das ist ihre gewählte Konfiguration:"
+"Dies ist ihre gewählte Konfiguration:"
msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
-msgstr "Pacman läuft bereits, warten für maximal 10min auf Beendigung."
+msgstr "Pacman läuft bereits, warten für maximal 10 Minuten auf Beendigung."
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "Existierendes Pacman lock wurde nicht beendet. Bitte beenden sie existierende Pacman Sessions um archinstall benützen zu können."
+msgstr "Existierendes Pacman lock wurde nicht beendet. Bitte beenden Sie existierende Pacman Sessions bevor archinstall benutzt wird."
msgid "Choose which optional additional repositories to enable"
-msgstr "Wählen sie welche zusätzlichen Repositories eingeschaltet werden sollen"
+msgstr "Wählen Sie aus, welche zusätzlichen Repositories verwendet werden sollen"
msgid "Add a user"
msgstr "Benutzerkonto hinzufügen"
@@ -479,7 +476,7 @@ msgid "Should {} be a superuser (sudoer)?"
msgstr "Soll {} ein superuser sein (sudoer)?"
msgid "Define users with sudo privilege: "
-msgstr "Geben sie super-user mit sudo Privilegien an: "
+msgstr "Geben Sie Superuser mit sudo Privilegien an: "
msgid "No network configuration"
msgstr "Keine Netzwerkkonfiguration"
@@ -494,7 +491,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Bitte wählen sie auf welcher Partition subvolumes eingerichtet werden sollen"
+"Bitte wählen Sie, auf welcher Partition Subvolumes eingerichtet werden sollen"
msgid "Manage btrfs subvolumes for current partition"
msgstr "Bearbeiten von Btrfs subvolumes für die aktuelle Partition"
@@ -509,25 +506,25 @@ msgid "Save user credentials"
msgstr "Benutzeranmeldedaten speichern"
msgid "Save disk layout"
-msgstr "Laufwerke-layout speichern"
+msgstr "Laufwerklayout speichern"
msgid "Save all"
-msgstr "Alle speichern"
+msgstr "Alles speichern"
msgid "Choose which configuration to save"
-msgstr "Bitte wählen sie eine Konfiguration aus welche gespeichert werden soll"
+msgstr "Bitte wählen Sie eine Konfiguration aus, welche gespeichert werden soll"
msgid "Enter a directory for the configuration(s) to be saved: "
-msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: "
+msgstr "Geben Sie einen Ordner an, in dem Konfigurationen gespeichert werden sollen: "
msgid "Not a valid directory: {}"
msgstr "Ordner existiert nicht: {}"
msgid "The password you are using seems to be weak,"
-msgstr "Das gewählte Passwort ist sehr schwach,"
+msgstr "Das gewählte Passwort ist schwach,"
msgid "are you sure you want to use it?"
-msgstr "wollen sie dieses wirklich verwenden?"
+msgstr "wollen Sie dieses Passwort wirklich verwenden?"
msgid "Optional repositories"
msgstr "Zus. Repositories"
@@ -539,7 +536,7 @@ msgid "Missing configurations:\n"
msgstr "Ausständige Konfigurationen:\n"
msgid "Either root-password or at least 1 superuser must be specified"
-msgstr "Entweder root Passwort oder wenigstens 1 super-user muss konfiguriert sein"
+msgstr "Entweder root Passwort oder mindestens ein Superuser muss konfiguriert sein"
msgid "Manage superuser accounts: "
msgstr "Superuser Konto bearbeiten: "
@@ -551,53 +548,53 @@ msgid " Subvolume :{:16}"
msgstr " Subvolume :{:16}"
msgid " mounted at {:16}"
-msgstr " Mounted an {:16}"
+msgstr " Eingehängt bei {:16}"
msgid " with option {}"
-msgstr "mit option {}"
+msgstr " mit Option {}"
msgid ""
"\n"
" Fill the desired values for a new subvolume \n"
msgstr ""
"\n"
-" Geben sie die gewünschten Werte für ein neues Subvolumen an \n"
+" Geben Sie die gewünschten Werte für ein neues Subvolume an \n"
msgid "Subvolume name "
-msgstr "Subvolumen name"
+msgstr "Subvolume name "
msgid "Subvolume mountpoint"
-msgstr "Subvolumen mountpunkt"
+msgstr "Subvolume Einhängeort"
msgid "Subvolume options"
-msgstr "Subvolumen Optionen"
+msgstr "Subvolume Optionen"
msgid "Save"
msgstr "Speichern"
msgid "Subvolume name :"
-msgstr "Subvolumen name :"
+msgstr "Subvolume name:"
msgid "Select a mount point :"
-msgstr "Bitte wählen Sie einen Mountpunkt aus"
+msgstr "Bitte wählen Sie einen Einhängeort aus:"
msgid "Select the desired subvolume options "
-msgstr "Wählen sie die gewünschte Subvolumen Optionen"
+msgstr "Wählen Sie die gewünschten Optionen für das Subvolumen "
msgid "Define users with sudo privilege, by username: "
-msgstr "Geben sie Benutzer mit sudo Privilegien an: "
+msgstr "Geben Sie Benutzer mit sudo Privilegien an: "
msgid "[!] A log file has been created here: {}"
-msgstr "[!] Eine Logdatei wurde erstellt: {}"
+msgstr "[!] Eine Logdatei wurde hier erstellt: {}"
msgid "Would you like to use BTRFS subvolumes with a default structure?"
-msgstr "Möchten sie Btrfs Subvolumen mit vorgebener Struktur?"
+msgstr "Möchten Sie btrfs Subvolumen mit vorgebener Struktur verwenden?"
msgid "Would you like to use BTRFS compression?"
-msgstr "Möchten sie Btrfs kompression?"
+msgstr "Möchten sie btrfs Komprimierung verwenden?"
msgid "Would you like to create a separate partition for /home?"
-msgstr "Möchten sie eine separate Partition für /home?"
+msgstr "Möchten Sie eine separate Partition für /home erstellen?"
msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
msgstr "Die ausgewählten Laufwerke haben nicht genug Speicherplatz für eine automatische Vorgabe\n"
@@ -612,10 +609,10 @@ msgid "Continue"
msgstr "Weiter"
msgid "yes"
-msgstr "ja"
+msgstr "Ja"
msgid "no"
-msgstr "nein"
+msgstr "Nein"
msgid "set: {}"
msgstr "gewählt: {}"
@@ -624,10 +621,10 @@ msgid "Manual configuration setting must be a list"
msgstr "Manuelle Konfiguration muss eine Liste sein"
msgid "No iface specified for manual configuration"
-msgstr "Kein Verbindung angegeben für eine manuelle Konfiguration"
+msgstr "Keine Verbindung angegeben für eine manuelle Konfiguration"
msgid "Manual nic configuration with no auto DHCP requires an IP address"
-msgstr "Manuelle Konfiguration für Netzwerverbindung mit keinem auto DHCP benötigt eien IP Addresse"
+msgstr "Manuelle Konfiguration für Netzwerverbindung mit keinem auto DHCP benötigt eine IP Addresse"
msgid "Add interface"
msgstr "Verbindung hinzufügen"
@@ -639,69 +636,68 @@ msgid "Delete interface"
msgstr "Verbindung löschen"
msgid "Select interface to add"
-msgstr "Wählen sie eine Verbindung welche hinzugefügt werden soll"
+msgstr "Wählen Sie eine Verbindung aus, welche hinzugefügt werden soll"
msgid "Manual configuration"
-msgstr "Manuelle konfiguration"
+msgstr "Manuelle Konfiguration"
msgid "Mark/Unmark a partition as compressed (btrfs only)"
-msgstr "Markieren/Unmarkieren Kompression von einer Partition (nur Btrfs) "
+msgstr "Partition als komprimiert makieren bzw. nicht komprimiert makieren (nur btrfs)"
msgid "The password you are using seems to be weak, are you sure you want to use it?"
-msgstr "Das gewählte Passwort ist schwach, möchten sie trotzdem fortfahren?"
+msgstr "Das gewählte Passwort ist schwach, möchten Sie trotzdem fortfahren?"
msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
-msgstr "Auswahl von Desktopumgebungen und tiling window managern, z.B. gnome, kde, sway"
+msgstr "Auswahl von Desktopumgebungen und tiling Fenstermanagern, z.B. gnome, kde, sway"
msgid "Select your desired desktop environment"
-msgstr "Wählen sie eine Desktopumgebung aus"
+msgstr "Wählen Sie ihre gewünschte Desktopumgebung aus"
msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
-msgstr "Eine sehr minimale Installation welche es erlaubt Arch Linux weitgehend anzupassen"
+msgstr "Eine sehr minimale Installation, welche es erlaubt Arch Linux selber nach eigenen Wünschen anzupassen."
msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
-msgstr "Auswahl von Serverpaketen welche installiert werden sollen, z.B. httpd, nginx, mariadb"
+msgstr "Auswahl von Serverpaketen welche installiert und aktiviert werden sollen, z.B. httpd, nginx, mariadb"
-#, fuzzy
msgid "Choose which servers to install, if none then a minimal installation will be done"
-msgstr "Wählen sie die gewünschten Server aus welche installiert werden sollen"
+msgstr "Wählen Sie die gewünschten Server aus, welche installiert werden sollen. Sonst wird eine Minimalinstallation durchgeführt"
msgid "Installs a minimal system as well as xorg and graphics drivers."
-msgstr "Installiert ein minimales System inklusive xorg und Graphiktreibern"
+msgstr "Installiert ein minimales System inklusive Xorg und Grafiktreibern."
msgid "Press Enter to continue."
msgstr "Drücken sie Enter um fortzufahren."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "Möchten sie chroot in das neu installierte System um noch weitere manuelle Konfigurationen forzunehmen?"
+msgstr "Möchten Sie in das neu installierte System über chroot zugreifen um noch weitere, manuelle Konfigurationen vorzunehmen?"
msgid "Are you sure you want to reset this setting?"
msgstr "Wollen Sie wirklich diese Konfiguration zurücksetzen?"
msgid "Select one or more hard drives to use and configure\n"
-msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen\n"
+msgstr "Bitte wählen Sie ein oder mehrere Laufwerk(e) aus, die konfiguriert und verwendet werden sollen\n"
msgid "Any modifications to the existing setting will reset the disk layout!"
msgstr "Modifikationen zur momentanen Konfiguration führen zu einem Löschen der Laufwerkskonfiguration!"
msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
-msgstr "Wenn sie die Laufwerkkonfiguration ändern, dann wird die Laufwer-layout zurückgesetzt. Sind sie sicher?"
+msgstr "Wenn Sie die Laufwerkskonfiguration ändern, dann wird das Laufwerkslayout zurückgesetzt. Sind Sie sicher?"
msgid "Save and exit"
-msgstr "Speichern und zurück"
+msgstr "Speichern und Beenden"
msgid ""
"{}\n"
"contains queued partitions, this will remove those, are you sure?"
msgstr ""
"{}\n"
-"enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?"
+"enthält Partitionen in der Warteschlange, diese werden damit entfernt, sind Sie sicher?"
msgid "No audio server"
-msgstr "Kein Audio server"
+msgstr "Kein Audioserver"
msgid "(default)"
-msgstr "(vorgegeben)"
+msgstr "(Standard)"
msgid "Use ESC to skip"
msgstr "ESC drücken um zu überspringen"
@@ -710,7 +706,7 @@ msgid ""
"Use CTRL+C to reset current selection\n"
"\n"
msgstr ""
-"Ctrl+C drücken um die Einstellungen zurückzusetzen\n"
+"Strg+C drücken um die aktuelle Auswahl zurückzusetzen\n"
"\n"
msgid "Copy to: "
@@ -726,170 +722,539 @@ msgid "Edit {}: "
msgstr "Bearbeiten {}: "
msgid "Add: "
-msgstr ""
+msgstr "Hinzufügen: "
-#, fuzzy
msgid "Value: "
-msgstr "Wert :"
+msgstr "Wert: "
msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
-msgstr ""
+msgstr "Sie können das Auswählen eines Laufwerks überspringen und die Laufwerkseinstellungen verwenden, welche am Einhängeort /mnt vorgefunden werden (experimentell)"
msgid "Select one of the disks or skip and use /mnt as default"
-msgstr ""
+msgstr "Wählen Sie ein Laufwerk aus oder überspringen, um /mnt als Standard zu verwenden"
-#, fuzzy
msgid "Select which partitions to mark for formatting:"
-msgstr ""
-"{}\n"
-"\n"
-"Bitte wählen sie welche Partition formatiert werden soll"
+msgstr "Bitte wählen Sie aus, welche Partitionen formatiert werden sollen:"
msgid "Use HSM to unlock encrypted drive"
-msgstr ""
+msgstr "HSM verwenden, um verschlüsselte Platte zu entsperren"
msgid "Device"
-msgstr ""
+msgstr "Gerät"
msgid "Size"
-msgstr ""
+msgstr "Größe"
msgid "Free space"
-msgstr ""
+msgstr "Freier Speicherplatz"
msgid "Bus-type"
-msgstr ""
+msgstr "Bus-Typ"
-#, fuzzy
msgid "Either root-password or at least 1 user with sudo privileges must be specified"
-msgstr "Entweder root Passwort oder wenigstens 1 super-user muss konfiguriert sein"
+msgstr "Entweder root Passwort oder mindestens ein Superuser muss konfiguriert sein"
-#, fuzzy
msgid "Enter username (leave blank to skip): "
-msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Ãœberspringen): "
+msgstr "Benutzernamen eingeben (leer lassen zum Ãœberspringen): "
msgid "The username you entered is invalid. Try again"
-msgstr ""
+msgstr "Der eingegebene Benutzername ist ungültig. Erneut versuchen"
-#, fuzzy
msgid "Should \"{}\" be a superuser (sudo)?"
-msgstr "Soll {} ein superuser sein (sudoer)?"
+msgstr "Soll {} ein Superuser sein (sudo)?"
-#, fuzzy
msgid "Select which partitions to encrypt"
-msgstr "Bitte wählen sie welche Partition verschlüsselt werden soll"
+msgstr "Bitte wählen Sie aus, welche Partitionen verschlüsselt werden sollen"
msgid "very weak"
-msgstr ""
+msgstr "Sehr schwach"
msgid "weak"
-msgstr ""
+msgstr "Schwach"
msgid "moderate"
-msgstr ""
+msgstr "Moderat"
msgid "strong"
-msgstr ""
+msgstr "Stark"
-#, fuzzy
msgid "Add subvolume"
-msgstr " Subvolume :{:16}"
+msgstr "Subvolume hinzufügen"
msgid "Edit subvolume"
-msgstr ""
+msgstr "Subvolume bearbeiten"
-#, fuzzy
msgid "Delete subvolume"
-msgstr "Benutzerkonto löschen"
+msgstr "Subvolume löschen"
msgid "Configured {} interfaces"
-msgstr ""
+msgstr "{} Schnittstellen konfiguriert"
msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr ""
+msgstr "Diese Option setzt die Nummer an parallelen Downloads, die während der Installtion durchgeführt werden"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
+"Geben Sie die Nummer an parallelen Downloads an.\n"
+" (Wert zwischen 1 und {max_downloads})\n"
+"Achtung:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr ""
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Maximalwert :{} (Erlaubt {} parallele Downloads, erlaubt {} Downloads gleichzeitig)"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr ""
+msgstr " - Minimalwert : 1 (Erlaubt einen parallelen Download, erlaubt zwei Downloads gleichzeitig)"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr ""
+msgstr " - Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt nur einen Download gleichzeitig)"
#, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr ""
+msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {max_downloads}, oder 0 zum deaktivieren]"
msgid "Parallel Downloads"
-msgstr ""
+msgstr "Parallele Downloads"
-#, fuzzy
msgid "ESC to skip"
-msgstr "ESC drücken um zu überspringen"
+msgstr "ESC zum Ãœberspringen"
msgid "CTRL+C to reset"
-msgstr ""
+msgstr "Strg+C zum zurücksetzen"
msgid "TAB to select"
-msgstr ""
+msgstr "TAB zum auswählen"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[Standardwert: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Um diese Übersetzung nutzen zu können, installieren Sie bitte manuell eine Schriftart, die diese Sprache unterstützt."
msgid "The font should be stored as {}"
-msgstr ""
+msgstr "Die Schriftart sollte als {} gespeichert werden"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall benötigt Root-Rechte zum ausführen. Siehe --help für mehr."
+
+msgid "Select an execution mode"
+msgstr "Wählen Sie einen Ausführmodus"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Konnte Profil nicht von der angegebenen URL fetchen: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Profile müssen einen eindeutige Namen haben, aber Profildefinition mit gleichem Namen gefunden: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Bitte wählen Sie ein oder mehrere Geräte aus, die konfiguriert und verwendet werden sollen"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Wenn Sie die Laufwerksauswahl zurücksetzen, dann wird das auch das Laufwerkslayout zurückgesetzt. Sind Sie sicher?"
+
+msgid "Existing Partitions"
+msgstr "Existierende Partitionen"
+
+msgid "Select a partitioning option"
+msgstr "Wählen Sie eine Partitionierungsoption aus"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Geben Sie das Stammverzeichnis der eingehängten Geräte an: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Minimaler Speicherplatz für /home Partition: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Minimaler Speicherplatz für Arch Linux Partition: {}GB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Dies ist eine Auflistung vorprogrammierter Profile (Backup), die es einfacher ermöglichen, Dinge wie Desktop-Umgebungen zu installieren"
+
+msgid "Current profile selection"
+msgstr "Momentane Profilauswahl"
+
+msgid "Remove all newly added partitions"
+msgstr "Alle neu hinzugefügten Partitionen entfernen"
+
+msgid "Assign mountpoint"
+msgstr "Einhängeort für Partition zuweisen"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Markieren bzw. nicht makieren zum formatieren (alle Daten werden gelöscht)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Als bootbar makieren bzw. nicht makieren"
+
+msgid "Change filesystem"
+msgstr "Dateisystem ändern"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Als komprimiert makieren bzw. als nicht komprimiert makieren"
+
+msgid "Set subvolumes"
+msgstr "Subvolumes setzen"
+
+msgid "Delete partition"
+msgstr "Partition löschen"
+
+msgid "Partition"
+msgstr "Partition"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Diese Partition ist aktuell verschlüsselt, zum formatieren muss ein Dateisystem angegeben werden"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Die Einhängeorte sind relativ zur Installation, boot würde beispielsweise /boot entsprechen."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Wenn der Einhängeort auf /boot gesetzt ist, wird die Partition ebenfalls als bootbar makiert."
+
+msgid "Mountpoint: "
+msgstr "Einhängeort: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Aktuell freie Sektoren auf dem Gerät {}:"
+
+msgid "Total sectors: {}"
+msgstr "Sektoren insgesamt: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Bitte geben Sie den Startsektor ein (Standard: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Bitte geben Sie den Endsektor der Partition ein (Prozent oder Blocknummer, Standard: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Dies wird alle neu hinzugefügten Partitionen entfernen, fortfahren?"
+
+msgid "Partition management: {}"
+msgstr "Partitionsverwaltung: {}"
+
+msgid "Total length: {}"
+msgstr "Gesamtlänge: {}"
-#, fuzzy
msgid "Encryption type"
-msgstr "Verschlüsselungspasswort angeben"
+msgstr "Verschlüsselungstyp"
msgid "Partitions"
-msgstr ""
+msgstr "Partitionen"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Kein HSM-Gerät verfügbar"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Bitte wählen sie welche Partition verschlüsselt werden soll"
+msgstr "Partitionen die verschlüsselt werden"
-#, fuzzy
msgid "Select disk encryption option"
-msgstr "Laufwerke-layout auswählen"
+msgstr "Laufwerksverschlüsselungsoption auswählen"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "FIDO2-Gerät für HSM auswählen"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Empfohlenes Partitionslayout verwenden"
+
+msgid "Manual Partitioning"
+msgstr "Manuelle Partitionierung"
+
+msgid "Pre-mounted configuration"
+msgstr "Voreingehängte Konfiguration"
+
+msgid "Unknown"
+msgstr "Unbekannt"
+
+msgid "Partition encryption"
+msgstr "Partitionsverschlüsselung"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formatiere {} in "
+
+msgid "↠Back"
+msgstr "↠Zurück"
+
+msgid "Disk encryption"
+msgstr "Festplattenverschlüsselung"
+
+msgid "Configuration"
+msgstr "Konfiguration"
+
+msgid "Password"
+msgstr "Passwort"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?"
+msgstr "Alle Einstellungen werden zurückgesetzt. Sind Sie sicher?"
msgid "Back"
+msgstr "Zurück"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Bitte einen Greeter (Begrüßer/Anmeldebildschirm) für das ausgewählte Profil auswählen: {}"
+
+msgid "Environment type: {}"
+msgstr "Umgebungstyp: {}"
+
+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 "Der proprietäre Nvidia-Treiber wird von Sway nicht unterstützt. Es ist wahrscheinlich, dass Fehler auftreten werden, trotzdem fortfahren?"
+
+msgid "Installed packages"
+msgstr "Installiere Pakete"
+
+msgid "Add profile"
+msgstr "Profil hinzufügen"
+
+msgid "Edit profile"
+msgstr "Profil bearbeiten"
+
+msgid "Delete profile"
+msgstr "Profil entfernen"
+
+msgid "Profile name: "
+msgstr "Profilname: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Der eingegebene Profilname wird bereits verwendet. Erneut versuchen"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Zusätzliche Pakete die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum Überspringen): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Dienste, die mit diesem Profil aktiviert werden sollen (Mit Leerzeichen trennen, leer lassen zum Ãœberspringen): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Soll dieses Profil für die Installation aktiviert werden?"
+
+msgid "Create your own"
+msgstr "Erstelle ein eigenes"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"\n"
+"Bitte wählen sie einen Grafiktreiber aus oder leer lassen um alle Quelloffenen Treiber zu installieren"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway benötigt Zugriff auf ihren Seat (Sammlung von Hardwaregeräten wie Tastatur, Maus, usw.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Option auswählen, um Sway Zugriff auf deine Hardware zu geben"
+
+msgid "Graphics driver"
+msgstr "Grafiktreiber"
+
+msgid "Greeter"
+msgstr "Greeter (Begrüßer/Anmeldebildschirm)"
+
+msgid "Please chose which greeter to install"
+msgstr "Bitte den zu installierenden Greeter (Begrüßer/Anmeldebildschirm) auswählen"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Dies ist eine Auflistung vorprogrammierter Standardprofile"
+
+msgid "Disk configuration"
+msgstr "Laufwerkskonfiguration"
+
+msgid "Profiles"
+msgstr "Profile"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Finde mögliche Pfade um Konfigurationsdateien zu speichern..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Ordner um Konfigurationsdateien zu erstellen auswählen"
+
+msgid "Add a custom mirror"
+msgstr "Benutzerdefinierten Spiegelserver hinzufügen"
+
+msgid "Change custom mirror"
+msgstr "Benutzerdefinierten Spiegelserver bearbeiten"
+
+msgid "Delete custom mirror"
+msgstr "Benutzerdefinierten Spiegelserver löschen"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Name eingeben (leer lassen zum Ãœberspringen): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "URL eingeben (leer lassen zum Ãœberspringen): "
+
+msgid "Select signature check option"
+msgstr "Signaturprüfungs-Option auswählen"
+
+msgid "Select signature option"
+msgstr "Signatur-Option auswählen"
+
+msgid "Custom mirrors"
+msgstr "Benutzerdefinierte Spiegelserver"
+
+msgid "Defined"
+msgstr "Definiert"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Benutzerkonfiguration (mit Laufwerkslayout) speichern"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Geben Sie einen Ordner an, in dem die Konfiguration(en) gespeichert werden sollen (TAB zum vervollständigen)\n"
+"Ordner: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Möchten Sie {} Konfigurationsdatei(en) in folgendem Ordner speichern?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "{} Konfigurationsdateien in {} speichern"
+
+msgid "Mirrors"
+msgstr "Spiegelserver"
+
+msgid "Mirror regions"
+msgstr "Spiegelserver-Regionen"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Maximalwert : {} ( Erlaubt {} parallele Downloads, erlaubt {max_downloads+1} Downloads gleichzeitig)"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]"
+
+msgid "Locales"
+msgstr "Lokalisierung"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager nutzen (notwendig um Internetverbindungen grafisch in GNOME und KDE einzustellen)"
#, fuzzy
-msgid "Password"
-msgstr "Root Passwort"
+msgid "Total: {} / {}"
+msgstr "Gesamtlänge: {}"
-msgid "Partition encryption"
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Bitte geben Sie den Startsektor ein (Standard: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Bitte geben Sie den Startsektor ein (Standard: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Manuelle Konfiguration"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Diese Option setzt die Nummer an parallelen Downloads, die während der Installtion durchgeführt werden"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
msgstr ""
+"Geben Sie die Nummer an parallelen Downloads an.\n"
+" (Wert zwischen 1 und {max_downloads})\n"
+"Achtung:"
-#~ msgid "Add :"
-#~ msgstr "Hinzufügen :"
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Maximalwert :{} (Erlaubt {} parallele Downloads, erlaubt {} Downloads gleichzeitig)"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt nur einen Download gleichzeitig)"
-#~ msgid "Value :"
-#~ msgstr "Wert :"
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]"
+
+#, fuzzy
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway benötigt Zugriff auf ihren Seat (Sammlung von Hardwaregeräten wie Tastatur, Maus, usw.)"
+
+#, fuzzy
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Option auswählen, um Sway Zugriff auf deine Hardware zu geben"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Möchten Sie Auslagerungsspeicher (Swap) als zram verwenden?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Profil entfernen"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Als bootbar makieren bzw. nicht makieren"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Möchten sie btrfs Komprimierung verwenden?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/el/LC_MESSAGES/base.mo b/archinstall/locales/el/LC_MESSAGES/base.mo
index 103eaf0c..98b71772 100644
--- a/archinstall/locales/el/LC_MESSAGES/base.mo
+++ b/archinstall/locales/el/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po
index efcd6b49..d7cde4e6 100644
--- a/archinstall/locales/el/LC_MESSAGES/base.po
+++ b/archinstall/locales/el/LC_MESSAGES/base.po
@@ -62,7 +62,7 @@ msgstr "ΓÏάψτε πεÏαιτέÏω πακέτα Ï€Ïος εγκατάστα
msgid "Copy ISO network configuration to installation"
msgstr "ΑντιγÏαφή διαμόÏφωση δικτÏου ISO στην εγκατάσταση"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "ΧÏήση NetworkManager (απαÏαίτητος για τη διαμόÏφωση του δικτÏου γÏαφικά σε GNOME και KDE)"
msgid "Select one network interface to configure"
@@ -75,10 +75,10 @@ msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
msgstr "Εισάγετε την IP και το υποδίκτυο για το {} (παÏάδειγμα: 192.168.0.5/24): "
msgid "Enter your gateway (router) IP address or leave blank for none: "
-msgstr "Εισάγετε τη διεÏθυνση IP του router σας ή αφήστε άδειο για καμία διεÏθυνση: "
+msgstr "Εισάγετε τη διεÏθυνση IP του router σας ή αφήστε κενό για καμία διεÏθυνση: "
msgid "Enter your DNS servers (space separated, blank for none): "
-msgstr "Εισάγετε τους διακομιστές DNS σας (χωÏισμένοι με κενό, αφήστε άδειο για κανέναν διακομιστή): "
+msgstr "Εισάγετε τους διακομιστές DNS σας (χωÏισμένοι με κενό, αφήστε κενό για κανέναν διακομιστή): "
msgid "Select which filesystem your main partition should use"
msgstr "Επιλέξτε ποιο σÏστημα αÏχείων θέλετε να χÏησιμοποιεί η κÏÏια διαμέÏιση"
@@ -127,7 +127,7 @@ msgid " * Partition mount-points are relative to inside the installation, the bo
msgstr " * Τα σημεία mount της διαμέÏισης είναι σχετικά ως Ï€Ïος το εσωτεÏικό της εγκατάστασης, για παÏάδειγμα το boot θα ήταν /boot."
msgid "Select where to mount partition (leave blank to remove mountpoint): "
-msgstr "Επιλέξτε που να γίνει mount η διαμέÏισιη (αφήστε άδειο για να διαγÏαφεί το σημείο mount): "
+msgstr "Επιλέξτε που να γίνει mount η διαμέÏισιη (αφήστε κενό για να διαγÏαφεί το σημείο mount): "
msgid ""
"{}\n"
@@ -193,10 +193,10 @@ msgid "Select one or more hard drives to use and configure"
msgstr "Επιλέξτε έναν ή πεÏισσότεÏους σκληÏοÏÏ‚ δίσκους Ï€Ïος χÏήση και διαμόÏφωση"
msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "Για την καλÏτεÏη συμβατότητα με το AMD υλισμικό σας, ίσως θέλετε να χÏησιμοποιήσετε είτε την \"όλα ανοιχτής πηγής\", είτε την AMD / ATI επιλογή."
+msgstr "Για την καλÏτεÏη συμβατότητα με το AMD υλισμικό σας, ίσως θέλετε να χÏησιμοποιήσετε είτε την \"όλα Î±Î½Î¿Î¹Ï‡Ï„Î¿Ï ÎºÏŽÎ´Î¹ÎºÎ±\", είτε την 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 "Για την καλÏτεÏη συμβατότητα με το Intel υλισμικό σας, ίσως θέλετε να χÏησιμοποιήσετε είτε την \"όλα ανοιχτής πηγής\", είτε την Intel επιλογή.\n"
+msgstr "Για την καλÏτεÏη συμβατότητα με το Intel υλισμικό σας, ίσως θέλετε να χÏησιμοποιήσετε είτε την \"όλα Î±Î½Î¿Î¹Ï‡Ï„Î¿Ï ÎºÏŽÎ´Î¹ÎºÎ±\", είτε την Intel επιλογή.\n"
msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
msgstr "Για την καλÏτεÏη συμβατότητα με το Nvidia υλισμικό σας, ίσως θέλετε να χÏησιμοποιήσετε τον ιδιόκτητο οδηγό της Nvidia.\n"
@@ -208,13 +208,13 @@ msgid ""
msgstr ""
"\n"
"\n"
-"Επιλέξτε έναν οδηγώ γÏαφικών ή αφήστε άδειο για να εγκατασταθοÏν όλοι οι οδηγοί ανοιχτής πηγής"
+"Επιλέξτε έναν οδηγό γÏαφικών ή αφήστε κενό για να εγκατασταθοÏν όλοι οι οδηγοί Î±Î½Î¿Î¹Ï‡Ï„Î¿Ï ÎºÏŽÎ´Î¹ÎºÎ±"
msgid "All open-source (default)"
-msgstr "Όλα ανοιχτής πηγής (Ï€ÏοκαθοÏισμένο)"
+msgstr "Όλα Î±Î½Î¿Î¹Ï‡Ï„Î¿Ï ÎºÏŽÎ´Î¹ÎºÎ± (Ï€ÏοκαθοÏισμένο)"
msgid "Choose which kernels to use or leave blank for default \"{}\""
-msgstr "Επιλέξτε ποια kernels να χÏησιμοποιηθοÏν ή αφήστε άδειο για το Ï€ÏοκαθοÏισμένο \"{}\""
+msgstr "Επιλέξτε ποιοι kernels να χÏησιμοποιηθοÏν ή αφήστε κενό για το Ï€ÏοκαθοÏισμένο \"{}\""
msgid "Choose which locale language to use"
msgstr "Επιλέξτε ποια τοπική γλώσσα να χÏησιμοποιηθεί"
@@ -370,13 +370,13 @@ msgid "Enter a encryption password for {}"
msgstr "Εισάγετε έναν κωδικό κÏυπτογÏάφησης για {}"
msgid "Enter disk encryption password (leave blank for no encryption): "
-msgstr "Εισάγετε κωδικό κÏυπτογÏάφησης δίσκου (αφήστε άδειο για καμία κÏυπτογÏάφηση): "
+msgstr "Εισάγετε κωδικό κÏυπτογÏάφησης δίσκου (αφήστε κενό για καμία κÏυπτογÏάφηση): "
msgid "Create a required super-user with sudo privileges: "
msgstr "ΔημιουÏγήστε έναν απαιτοÏμενο υπεÏχÏήστη με δικαιώματα sudo: "
msgid "Enter root password (leave blank to disable root): "
-msgstr "Εισάγετε τον κωδικό root (αφήστε άδειο για να απενεÏγοποιηθεί το root): "
+msgstr "Εισάγετε τον κωδικό root (αφήστε κενό για να απενεÏγοποιηθεί το root): "
msgid "Password for user \"{}\": "
msgstr "Κωδικός για τον χÏήστη \"{}\": "
@@ -395,7 +395,7 @@ msgstr ""
"Για πεÏισσότεÏες πληÏοφοÏίες, παÏακαλώ ελέγξτε το Arch wiki"
msgid "Enter a username to create an additional user (leave blank to skip): "
-msgstr "Εισάγετε ένα όνομα χÏήστη για να δημιουÏγήσετε έναν ακόμα χÏήστη (αφήστε άδειο για παÏάλειψη): "
+msgstr "Εισάγετε ένα όνομα χÏήστη για να δημιουÏγήσετε έναν ακόμα χÏήστη (αφήστε κενό για παÏάλειψη): "
msgid "Use ESC to skip\n"
msgstr "ΧÏησιμοποιήστε ESC για παÏάλειψη\n"
@@ -793,18 +793,17 @@ msgstr "ΔιαμοÏφωμένες {} διεπαφές"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Αυτή η επιλογή θέτει τον αÏιθμό των παÏάλληλων λήψεων που μποÏοÏν να συμβοÏν κατά την εγκατάσταση"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Εισάγετε τον αÏιθμό των παÏάλληλων λήψεων Ï€Ïος ενεÏγοποίηση.\n"
-" (Εισάγετε μία τιμή από 1 μέχÏι {max_downloads})\n"
+" (Εισάγετε μία τιμή από 1 μέχÏι {})\n"
"Σημείωση:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Μέγιστη τιμή : {max_downloads} ( ΕπιτÏέπει {max_downloads} παÏάλληλες λήψεις, επιτÏέπει {max_downloads+1} λήψεις σε μία στιγμή )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Μέγιστη τιμή : {} ( ΕπιτÏέπει {} παÏάλληλες λήψεις, επιτÏέπει {} λήψεις σε μία στιγμή )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Ελάχιστη τιμή : 1 ( ΕπιτÏέπει 1 παÏάλληλη λήψη, επιτÏέπει 2 λήψεις σε μία στιγμή )"
@@ -812,7 +811,7 @@ msgstr " - Ελάχιστη τιμή : 1 ( ΕπιτÏέπει 1 παÏάλληÎ
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
msgstr " - ΑπενεÏγοποίηση/ΠÏοκαθοÏισμένο : 0 ( ΑπενεÏγοποιεί τις παÏάλληλες λήψεις, επιτÏέπει μόνο 1 λήψη σε μία στιγμή )"
-#, python-brace-format
+#, fuzzy, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
msgstr "Μη έγκυÏη είσοδος! ΠÏοσπαθήστε ξανά με μία έγκυÏη είσοδο [1 μέχÏι {max_downloads}, ή 0 για απενεÏγοποίηση]"
@@ -824,18 +823,137 @@ msgid "ESC to skip"
msgstr "ΧÏησιμοποιήστε ESC για παÏάλειψη"
msgid "CTRL+C to reset"
-msgstr ""
+msgstr "CTRL+C για επαναφοÏά"
msgid "TAB to select"
-msgstr ""
+msgstr "TAB για επιλογή"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[ΠÏοεπιλεγμένη τιμή: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Για να μποÏείτε να χÏησιμοποιήσετε αυτή την μετάφÏαση, παÏακαλώ εγκαταστήστε χειÏοκίνητα την γÏαμματοσειÏά που υποστηÏίζει την γλώσσα."
msgid "The font should be stored as {}"
+msgstr "Η γÏαμματοσειÏά θα Ï€Ïέπει να αποθηκευτεί ως {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Το Archinstall απαιτεί δικαιώματα υπεÏχÏήστη για να εκτελεστεί. Δείτε --help for more."
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "Επιλέξτε μία ενέÏγεια για '{}'"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "ΑδÏνατη η λήψη Ï€Ïοφίλ από το συγκεκÏιμένο url: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Τα Ï€Ïοφίλ Ï€Ïέπει να έχουν μοναδικό όνομα, αλλά βÏέθηκαν οÏισμοί Ï€Ïοφίλ με διπλό όνομα: {}"
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "Επιλέξτε έναν ή πεÏισσότεÏους σκληÏοÏÏ‚ δίσκους Ï€Ïος χÏήση και διαμόÏφωση"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Αν επαναφέÏετε την επιλογή σκληÏÎ¿Ï Î´Î¯ÏƒÎºÎ¿Ï… αυτό επίσης θα επαναφέÏει την Ï„Ïέχουσα διάταξη δίσκου. Είστε σίγουÏη/ος;"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "ΠÏοσθέτωντας τη διαμέÏιση...."
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "ΔιαγÏαφή διαμέÏισης"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Εισάγετε έναν φάκελο για την αποθήκευση της/ων διαμόÏφωση/ων: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Ελάχιστη χωÏητικότητα για τη διαμέÏιση /home: {}GB\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Ελάχιστη χωÏητικότητα για τη διαμέÏιση Arch Linux: {}GB"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Αυτή είναι μία λίστα με Ï€Ïο-Ï€ÏογÏαμματισμένα Ï€Ïοφίλ, που μποÏεί να κάνουν την εγκατάσταση Ï€Ïαγμάτων όπως πεÏιβάλλοντα επιφάνειας εÏγασίας πιο εÏκολη"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "ΤÏέχουσα διάταξη διαμέÏισης"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "ΑφαίÏεση όλων των νέων διαμεÏίσεων"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "ΕκχώÏηση σημείου mount για μία διαμέÏιση"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Σημείωση/ΞεμαÏκάÏισμα διαμέÏισης Ï€Ïος μοÏφοποίηση (διαγÏάφει τα δεδομένα)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Σημείωση/ΞεμαÏκάÏισμα ως bootable"
+
+msgid "Change filesystem"
+msgstr "Αλλαγή συστήματος αÏχείων"
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "Σημείωση/ΞεμαÏκάÏισμα μίας διαμέÏισως ως συμπιεσμένη (μόνο για btrfs)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "ΟÏισμός υποόγκων"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "ΔιαγÏαφή διαμέÏισης"
+
+msgid "Partition"
+msgstr "ΔιαμέÏιση"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Αυτή η διαμέÏιση είναι κÏυπτογÏαφημένη, για μοÏφοποίηση Ï€Ïέπει να οÏιστεί ένα σÏστημα αÏχείων"
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Τα σημεία mount της διαμέÏισης είναι σχετικά ως Ï€Ïος το εσωτεÏικό της εγκατάστασης, για παÏάδειγμα το boot θα ήταν /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "ΣÏνολο sectors: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "Εισάγετε τον start sector (ποσοστό ή αÏιθμό block, Ï€ÏοκαθοÏισμένο {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Εισάγετε τον end sector της διαμέÏισης (ποσοστό ή αÏιθμό block, πχ: {}) "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Αυτή η ενέÏγεια θα αφαιÏέσει τις νέες διαμεÏίσεις, συνέχεια;"
+
+msgid "Partition management: {}"
+msgstr "ΔιαχείÏιση διαμέÏισης"
+
+msgid "Total length: {}"
msgstr ""
#, fuzzy
@@ -843,40 +961,340 @@ msgid "Encryption type"
msgstr "Κωδικός κÏυπτογÏάφησης"
msgid "Partitions"
-msgstr ""
+msgstr "ΔιαμεÏίσεις"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Καμία διαθέσιμη συσκευή HSM"
#, fuzzy
msgid "Partitions to be encrypted"
msgstr "Επιλέξτε ποιες διαμεÏίσεις να κÏυπτογÏαφηθοÏν."
msgid "Select disk encryption option"
-msgstr ""
+msgstr "Επιλογή κÏυπτογÏάφησης δίσκου"
msgid "Select a FIDO2 device to use for HSM"
msgstr ""
#, fuzzy
+msgid "Use a best-effort default partition layout"
+msgstr "ΔιαγÏαφή όλων των επιλεγμένων δίσκων και χÏήση μίας Ï€ÏοκαθοÏισμένης διάταξης βέλτιστης Ï€Ïοσπάθειας"
+
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "ΧειÏοκίνητη διαμόÏφωση"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "Καμία διαμόÏφωση"
+
+msgid "Unknown"
+msgstr "Άγνωστο"
+
+msgid "Partition encryption"
+msgstr "ΚÏυπτογÏάφηση διαμέÏισης"
+
+msgid " ! Formatting {} in "
+msgstr " ! ΜοÏφοποίηση {} σε "
+
+msgid "↠Back"
+msgstr "↠Πίσω"
+
+msgid "Disk encryption"
+msgstr "ΚÏυπτογÏάφηση δίσκου"
+
+#, fuzzy
+msgid "Configuration"
+msgstr "Καμία διαμόÏφωση"
+
+#, fuzzy
+msgid "Password"
+msgstr "Κωδικός root"
+
+#, fuzzy
msgid "All settings will be reset, are you sure?"
msgstr "{} πεÏιέχει διαμεÏίσεις στην ουÏά, αυτό θα τις διαγÏάψει, είστε σίγουÏη/ος;"
msgid "Back"
+msgstr "Πίσω"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
msgstr ""
-msgid "Disk encryption"
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
#, fuzzy
-msgid "Password"
-msgstr "Κωδικός root"
+msgid "Installed packages"
+msgstr "Εγκατεστημένα πακέτα"
-msgid "Partition encryption"
+#, fuzzy
+msgid "Add profile"
+msgstr "ΠÏοσθήκη Ï€Ïοφίλ"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "ΕπεξεÏγασία Ï€Ïοφίλ"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "ΔιαγÏαφή Ï€Ïοφίλ"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "Όνομα Ï€Ïοφίλ: "
+
+#, fuzzy
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Το όνομα χÏήστη που εισάγατε δεν είναι έγκυÏο. ΠÏοσπαθήστε ξανά"
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "ΓÏάψτε πεÏαιτέÏω πακέτα Ï€Ïος εγκατάσταση (χωÏισμένα με κενό, αφήστε κενό για να παÏαληφθεί): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "ΓÏάψτε πεÏαιτέÏω πακέτα Ï€Ïος εγκατάσταση (χωÏισμένα με κενό, αφήστε κενό για να παÏαληφθεί): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "ΕνεÏγοποίηση του Ï€Ïοφίλ για εγκατάσταση;"
+
+msgid "Create your own"
+msgstr "ΔημιουÏγήστε δικό σας"
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Επιλέξτε έναν οδηγό γÏαφικών ή αφήστε κενό για να εγκατασταθοÏν όλοι οι οδηγοί Î±Î½Î¿Î¹Ï‡Ï„Î¿Ï ÎºÏŽÎ´Î¹ÎºÎ±"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr "Οδηγός γÏαφικών"
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "Καμία διαμόÏφωση"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "ΠÏοφίλ"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Επιλέξτε έναν ή πεÏισσότεÏους σκληÏοÏÏ‚ δίσκους Ï€Ïος χÏήση και διαμόÏφωση"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "ΠÏοσθήκη custom mirror"
+
+msgid "Change custom mirror"
+msgstr "Αλλαγή custom mirror"
+
+msgid "Delete custom mirror"
+msgstr "ΔιαγÏαφή custom mirror"
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "Εισάγετε όνομα χÏήστη (αφήστε κενό για παÏάλειψη): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "Εισάγετε όνομα χÏήστη (αφήστε κενό για παÏάλειψη): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "Επιλέξτε διεπαφή Ï€Ïος Ï€Ïοσθήκη"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "Επιλέξτε διεπαφή Ï€Ïος Ï€Ïοσθήκη"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "Αποθήκευση διαμόÏφωσης χÏήστη"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "Εισάγετε έναν φάκελο για την αποθήκευση της/ων διαμόÏφωση/ων: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Εισάγετε τον start sector (ποσοστό ή αÏιθμό block, Ï€ÏοκαθοÏισμένο {}): "
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "Αποθήκευση διαμόÏφωσης"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Εισάγετε τον end sector της διαμέÏισης (ποσοστό ή αÏιθμό block, πχ: {}) "
+#, fuzzy
+msgid "Mirrors"
+msgstr "ΠεÏιοχή mirror"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "ΠεÏιοχή mirror"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Μέγιστη τιμή : {} ( ΕπιτÏέπει {} παÏάλληλες λήψεις, επιτÏέπει {} λήψεις σε μία στιγμή )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Μη έγκυÏη είσοδος! ΠÏοσπαθήστε ξανά με μία έγκυÏη είσοδο [1 μέχÏι {}, ή 0 για απενεÏγοποίηση]"
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "ΧÏήση NetworkManager (απαÏαίτητος για τη διαμόÏφωση του δικτÏου γÏαφικά σε GNOME και KDE)"
+
+msgid "Total: {} / {}"
+msgstr "ΣÏνολο: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Αν δεν έχει δωθεί μονάδα, η τιμή εÏμηνεÏεται ως sectors"
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Εισάγετε τον start sector (ποσοστό ή αÏιθμό block, Ï€ÏοκαθοÏισμένο {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Εισάγετε τον start sector (ποσοστό ή αÏιθμό block, Ï€ÏοκαθοÏισμένο {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr "Μονοπάτι"
+
+msgid "Manufacturer"
+msgstr "Κατασκευαστής"
+
+msgid "Product"
+msgstr "ΠÏοϊόν"
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "ΧειÏοκίνητη διαμόÏφωση"
+
+msgid "Type"
+msgstr "ΤÏπος"
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Αυτή η επιλογή θέτει τον αÏιθμό των παÏάλληλων λήψεων που μποÏοÏν να συμβοÏν κατά την εγκατάσταση"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Εισάγετε τον αÏιθμό των παÏάλληλων λήψεων Ï€Ïος ενεÏγοποίηση.\n"
+" (Εισάγετε μία τιμή από 1 μέχÏι {})\n"
+"Σημείωση:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Μέγιστη τιμή : {} ( ΕπιτÏέπει {} παÏάλληλες λήψεις, επιτÏέπει {} λήψεις σε μία στιγμή )"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - ΑπενεÏγοποίηση/ΠÏοκαθοÏισμένο : 0 ( ΑπενεÏγοποιεί τις παÏάλληλες λήψεις, επιτÏέπει μόνο 1 λήψη σε μία στιγμή )"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Μη έγκυÏη είσοδος! ΠÏοσπαθήστε ξανά με μία έγκυÏη είσοδο [1 μέχÏι {}, ή 0 για απενεÏγοποίηση]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr "Διαλέξτε μία επιλογή για να δώσετε Ï€Ïόσβαση του Hyprland στο υλισμικό σας"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Θα θέλατε να χÏησιμοποιήσετε swap με zram;"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "ΠεÏιμένοντας να ολοκληÏωθεί ο συγχÏονισμός του χÏόνου (timedatectl show)"
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Ο συγχÏονισμός χÏόνου δεν ολοκληÏώνεται, όσο πεÏιμένετε - ελέγξτε την τεκμηÏίωση για λÏσεις: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "ΠαÏαλείποντας την αναμονή για αυτόματο συγχÏονισμό χÏόνου (μποÏεί να Ï€Ïοκαλέσει Ï€Ïοβλήματα αν ο χÏόνος δεν είναι συγχÏονισμένος κατά την εγκατάσταση)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "ΔιαγÏαφή Ï€Ïοφίλ"
+
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Ο συγχÏονισμός χÏόνου δεν ολοκληÏώνεται, όσο πεÏιμένετε - ελέγξτε την τεκμηÏίωση για λÏσεις: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Σημείωση/ΞεμαÏκάÏισμα ως bootable"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Θα θέλατε να χÏησιμοποιήσετε συμπίεση BTRFS;"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/en/LC_MESSAGES/base.mo b/archinstall/locales/en/LC_MESSAGES/base.mo
index e6ac80c2..7275098e 100644
--- a/archinstall/locales/en/LC_MESSAGES/base.mo
+++ b/archinstall/locales/en/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po
index f1722ef9..896af58f 100644
--- a/archinstall/locales/en/LC_MESSAGES/base.po
+++ b/archinstall/locales/en/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.0.1\n"
+"X-Generator: Poedit 3.3\n"
msgid "[!] A log file has been created here: {} {}"
msgstr ""
@@ -62,7 +62,7 @@ msgstr ""
msgid "Copy ISO network configuration to installation"
msgstr ""
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr ""
msgid "Select one network interface to configure"
@@ -747,14 +747,13 @@ msgstr ""
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr ""
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
msgstr ""
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
@@ -788,6 +787,105 @@ msgstr ""
msgid "The font should be stored as {}"
msgstr ""
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+msgid "Select an execution mode"
+msgstr ""
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+msgid "Select one or more devices to use and configure"
+msgstr ""
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+msgid "Existing Partitions"
+msgstr ""
+
+msgid "Select a partitioning option"
+msgstr ""
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr ""
+
+msgid "Current profile selection"
+msgstr ""
+
+msgid "Remove all newly added partitions"
+msgstr ""
+
+msgid "Assign mountpoint"
+msgstr ""
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+msgid "Mark/Unmark as compressed"
+msgstr ""
+
+msgid "Set subvolumes"
+msgstr ""
+
+msgid "Delete partition"
+msgstr ""
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr ""
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+msgid "Total sectors: {}"
+msgstr ""
+
+msgid "Enter the start sector (default: {}): "
+msgstr ""
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr ""
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
msgid "Encryption type"
msgstr ""
@@ -806,17 +904,277 @@ msgstr ""
msgid "Select a FIDO2 device to use for HSM"
msgstr ""
-msgid "All settings will be reset, are you sure?"
+msgid "Use a best-effort default partition layout"
msgstr ""
-msgid "Back"
+msgid "Manual Partitioning"
+msgstr ""
+
+msgid "Pre-mounted configuration"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
+msgid "Configuration"
+msgstr ""
+
msgid "Password"
msgstr ""
-msgid "Partition encryption"
+msgid "All settings will be reset, are you sure?"
+msgstr ""
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+msgid "Installed packages"
+msgstr ""
+
+msgid "Add profile"
+msgstr ""
+
+msgid "Edit profile"
+msgstr ""
+
+msgid "Delete profile"
+msgstr ""
+
+msgid "Profile name: "
+msgstr ""
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+msgid "Disk configuration"
+msgstr ""
+
+msgid "Profiles"
+msgstr ""
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr ""
+
+msgid "Add a custom mirror"
+msgstr ""
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+msgid "Enter name (leave blank to skip): "
+msgstr ""
+
+msgid "Enter url (leave blank to skip): "
+msgstr ""
+
+msgid "Select signature check option"
+msgstr ""
+
+msgid "Select signature option"
+msgstr ""
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+msgid "Save user configuration (including disk layout)"
+msgstr ""
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+msgid "Saving {} configuration files to {}"
+msgstr ""
+
+msgid "Mirrors"
+msgstr ""
+
+msgid "Mirror regions"
+msgstr ""
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr ""
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr ""
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+msgid "Enter start (default: sector {}): "
+msgstr ""
+
+msgid "Enter end (default: {}): "
+msgstr ""
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "Would you like to use unified kernel images?"
+msgstr ""
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+msgid "Selected profiles: "
+msgstr ""
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Mark/Unmark as nodatacow"
+msgstr ""
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr ""
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
msgstr ""
diff --git a/archinstall/locales/es/LC_MESSAGES/base.mo b/archinstall/locales/es/LC_MESSAGES/base.mo
index 7194020e..93287c51 100644
--- a/archinstall/locales/es/LC_MESSAGES/base.mo
+++ b/archinstall/locales/es/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po
index 20101de0..55dc9576 100644
--- a/archinstall/locales/es/LC_MESSAGES/base.po
+++ b/archinstall/locales/es/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.0.1\n"
+"X-Generator: Poedit 3.4.2\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] Se ha creado un archivo de registro aquí: {} {}"
@@ -39,7 +39,7 @@ msgid "Should this user be a superuser (sudoer)?"
msgstr "Debería este usuario ser un superusuario (sudoer)?"
msgid "Select a timezone"
-msgstr "Zona horaria"
+msgstr "Seleccione una zona horaria"
msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
msgstr "Te gustaría usar GRUB como gestor de arranque en lugar de systemd-boot?"
@@ -54,7 +54,7 @@ msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr
msgstr "Solo paquetes como base, base-devel, linux, linux-firmware, efibootmgr y paquetes opcionales de perfil se instalan."
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "Si desea un navegador web, como firefox o chromium, puede especificarlo en el siguiente diálogo."
+msgstr "Si desea un navegador web, como firefox o chromium, puede especificarlo en el siguiente mensaje."
msgid "Write additional packages to install (space separated, leave blank to skip): "
msgstr "Escriba paquetes adicionales para instalar (separados por espacios, deja en blanco para omitir): "
@@ -62,14 +62,14 @@ msgstr "Escriba paquetes adicionales para instalar (separados por espacios, deja
msgid "Copy ISO network configuration to installation"
msgstr "Copiar la configuración de red ISO a la instalación"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)"
msgid "Select one network interface to configure"
msgstr "Seleccione una interfaz de red para configurar"
msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
-msgstr "Seleccione qué modo configurar para \"{}\" o salte para usar el modo predeterminado \"{}\""
+msgstr "Seleccione qué modo configurar para \"{}\" u omita para usar el modo predeterminado \"{}\""
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
msgstr "Escriba la IP y subred para {} (ejemplo: 192.168.0.5/24): "
@@ -97,10 +97,10 @@ msgid "Enter a desired filesystem type for the partition"
msgstr "Ingrese un tipo de sistema de archivos deseado para la partición"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Ingrese la ubicación de inicio (en unidades divididas: s, GB, %, etc. ; predeterminado: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Ingrese la ubicación final (en unidades divididas: s, GB, %, etc. ; ej.: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} contiene particiones en cola, esto eliminará esas particiones, ¿estás seguro?"
@@ -124,10 +124,10 @@ msgstr ""
"Seleccione por índice la ubicación de la partición a montar"
msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
-msgstr " * Los puntos de montaje de partición son relativos a la instalación, el arranque sería /boot como ejemplo."
+msgstr " * Los puntos de montaje de la partición son relativos al interior de la instalación, el arranque sería /boot como ejemplo."
msgid "Select where to mount partition (leave blank to remove mountpoint): "
-msgstr "Seleccione dónde montar la partición (déjelo en blanco para eliminar el punto de montaje): "
+msgstr "Seleccione dónde montar la partición (deje en blanco para eliminar el punto de montaje): "
msgid ""
"{}\n"
@@ -181,7 +181,7 @@ msgid "Select what you wish to do with the selected block devices"
msgstr "Seleccione lo que desea hacer con los dispositivos de bloque seleccionados"
msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
-msgstr "Esta es una lista de perfiles pre-programados, pueden facilitar la instalación de aplicaciones como entornos de escritorio"
+msgstr "Esta es una lista de perfiles preprogramados que podrían facilitar la instalación de cosas como entornos de escritorio"
msgid "Select keyboard layout"
msgstr "Seleccione la distribución del teclado"
@@ -193,7 +193,7 @@ msgid "Select one or more hard drives to use and configure"
msgstr "Seleccione uno o más discos duros para usar y configurar"
msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "Para obtener la mejor compatibilidad con su hardware AMD, es posible que desee utilizar las opciones de código abierto o AMD/ATI."
+msgstr "Para obtener la mejor compatibilidad con su hardware AMD, es posible que desee utilizar las opciones de código abierto o AMD / ATI."
msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
msgstr "Para obtener la mejor compatibilidad con su hardware Intel, es posible que desee utilizar las opciones de código abierto o de Intel.\n"
@@ -330,7 +330,7 @@ msgid "Mark/Unmark a partition as encrypted"
msgstr "Marcar/Desmarcar una partición como encriptada"
msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
-msgstr "Marcar/Desmarcar una partición como bootable (automática para /boot)"
+msgstr "Marcar/Desmarcar una partición como arrancable (automática para /boot)"
msgid "Set desired filesystem for a partition"
msgstr "Establecer el sistema de archivos deseado para una partición"
@@ -364,19 +364,19 @@ msgid "Suggest partition layout"
msgstr "Sugerir el diseño de partición"
msgid "Enter a password: "
-msgstr "Introduzca una contraseña: "
+msgstr "Ingrese una contraseña: "
msgid "Enter a encryption password for {}"
msgstr "Introduzca una contraseña de cifrado para {}"
msgid "Enter disk encryption password (leave blank for no encryption): "
-msgstr "Introduzca la contraseña de cifrado de disco (dejar en blanco para no cifrar): "
+msgstr "Ingrese la contraseña de cifrado de disco (deje en blanco para no cifrar): "
msgid "Create a required super-user with sudo privileges: "
msgstr "Crear un super-usuario requerido con privilegios sudo: "
msgid "Enter root password (leave blank to disable root): "
-msgstr "Introduzca la contraseña de root (dejar en blanco para desactivar root): "
+msgstr "Ingrese la contraseña de root (deje en blanco para deshabilitar root): "
msgid "Password for user \"{}\": "
msgstr "Contraseña para el usuario “{}â€: "
@@ -390,20 +390,22 @@ msgstr "Te gustaría utilizar la sincronización automática de hora (NTP) con l
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 "La hora del hardware y otros pasos post-configuración pueden ser necesarios para que NTP funcione. Para más información, por favor, consulte la wiki de Arch"
+msgstr ""
+"La hora del hardware y otros pasos post-configuración pueden ser necesarios para que NTP funcione. \n"
+"Para más información, por favor, consulte la wiki de Arch"
msgid "Enter a username to create an additional user (leave blank to skip): "
-msgstr "Introduzca un nombre de usuario para crear un usuario adicional (dejar en blanco para saltar): "
+msgstr "Introduzca un nombre de usuario para crear un usuario adicional (deje en blanco para saltar): "
msgid "Use ESC to skip\n"
-msgstr "Use ESC para saltar\n"
+msgstr "Use ESC para omitir\n"
msgid ""
"\n"
" Choose an object from the list, and select one of the available actions for it to execute"
msgstr ""
"\n"
-"Elija un objeto de la lista y seleccione una de las acciones disponibles para que se ejecute"
+"Elija un objeto de la lista y seleccione una de las acciones disponibles para ejecutar"
msgid "Cancel"
msgstr "Cancelar"
@@ -443,7 +445,7 @@ msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate
msgstr "Pacman ya se está ejecutando, esperando un máximo de 10 minutos para que finalice."
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "El bloqueo de pacman preexistente nunca salió. Limpie cualquier sesión de pacman existente antes de usar archinstall."
+msgstr "El bloqueo de Pacman preexistente nunca se cerró. Limpie cualquier sesión de Pacman existente antes de usar archinstall."
msgid "Choose which optional additional repositories to enable"
msgstr "Elija qué repositorios adicionales opcionales habilitar"
@@ -513,7 +515,7 @@ msgid "Choose which configuration to save"
msgstr "Elija qué configuración guardar"
msgid "Enter a directory for the configuration(s) to be saved: "
-msgstr "Introduzca un directorio para guardar la(s) configuración(es): "
+msgstr "Ingrese un directorio para guardar la(s) configuración(es): "
msgid "Not a valid directory: {}"
msgstr "No es un directorio válido: {}"
@@ -525,7 +527,7 @@ msgid "are you sure you want to use it?"
msgstr "¿Estás seguro de que quieres usarlo?"
msgid "Optional repositories"
-msgstr "Repositorios adicionales"
+msgstr "Repositorios opcionales"
msgid "Save configuration"
msgstr "Guardar configuración"
@@ -534,7 +536,7 @@ msgid "Missing configurations:\n"
msgstr "Configuraciones que faltan:\n"
msgid "Either root-password or at least 1 superuser must be specified"
-msgstr "Se debe especificar una contraseña raíz o al menos 1 superusuario"
+msgstr "Se debe especificar una contraseña de root o al menos 1 superusuario"
msgid "Manage superuser accounts: "
msgstr "Administrar cuentas de superusuario: "
@@ -556,7 +558,7 @@ msgid ""
" Fill the desired values for a new subvolume \n"
msgstr ""
"\n"
-"Rellene los valores deseados para un nuevo subvolumen\n"
+"Complete los valores deseados para un nuevo subvolumen\n"
msgid "Subvolume name "
msgstr "Nombre del subvolumen "
@@ -616,7 +618,7 @@ msgid "set: {}"
msgstr "establecer: {}"
msgid "Manual configuration setting must be a list"
-msgstr "El ajuste de configuración manual debe ser una lista"
+msgstr "La configuración manual debe ser una lista."
msgid "No iface specified for manual configuration"
msgstr "No se especificó iface para la configuración manual"
@@ -657,7 +659,6 @@ msgstr "Una instalación muy básica que te permite personalizar Arch Linux como
msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
msgstr "Proporciona una selección de varios paquetes de servidor para instalar y habilitar, p.e. httpd, nginx, mariadb"
-#, fuzzy
msgid "Choose which servers to install, if none then a minimal installation will be done"
msgstr "Elija qué servidores instalar, si no hay ninguno, se realizará una instalación mínima"
@@ -668,7 +669,7 @@ msgid "Press Enter to continue."
msgstr "Presione Entrar para continuar."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "¿Le gustaría hacer chroot en la instalación recién creada y realizar la configuración posterior a la instalación?"
+msgstr "¿Le gustaría acceder a la instalación recién creada y realizar la configuración posterior a la instalación?"
msgid "Are you sure you want to reset this setting?"
msgstr "¿Está seguro de que desea restablecer esta configuración?"
@@ -699,7 +700,7 @@ msgid "(default)"
msgstr "(predeterminado)"
msgid "Use ESC to skip"
-msgstr "Use ESC para saltar"
+msgstr "Use ESC para omitir"
msgid ""
"Use CTRL+C to reset current selection\n"
@@ -736,7 +737,7 @@ msgid "Select which partitions to mark for formatting:"
msgstr "Seleccione qué particiones marcar para formatear:"
msgid "Use HSM to unlock encrypted drive"
-msgstr "Utilice HSM para desbloquear la unidad cifrada"
+msgstr "Usar HSM para desbloquear la unidad cifrada"
msgid "Device"
msgstr "Dispositivo"
@@ -766,119 +767,488 @@ msgid "Select which partitions to encrypt"
msgstr "Seleccione qué particiones cifrar"
msgid "very weak"
-msgstr ""
+msgstr "muy débil"
msgid "weak"
-msgstr ""
+msgstr "débil"
msgid "moderate"
-msgstr ""
+msgstr "moderado"
msgid "strong"
-msgstr ""
+msgstr "fuerte"
-#, fuzzy
msgid "Add subvolume"
-msgstr " Subvolumen :{:16}"
+msgstr "Agregar subvolumen"
msgid "Edit subvolume"
-msgstr ""
+msgstr "Editar subvolumen"
-#, fuzzy
msgid "Delete subvolume"
-msgstr "Eliminar usuario"
+msgstr "Eliminar subvolumen"
msgid "Configured {} interfaces"
-msgstr ""
+msgstr "{} interfaces configuradas"
msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr ""
+msgstr "Esta opción habilita la cantidad de descargas paralelas que pueden ocurrir durante la instalación"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
+"Ingrese el número de descargas paralelas que se habilitarán.\n"
+" (Ingrese un valor entre 1 y {})\n"
+"Nota:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr ""
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Valor máximo : {} ( Habilita {} descargas paralelas, permite {} descargas simultáneas )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr ""
+msgstr " - Valor mínimo : 1 ( Habilita 1 descarga paralela, permite 2 descargas simultáneas )"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr ""
+msgstr " - Deshabilitar/Predeterminado : 0 ( Deshabilita la descarga paralela, permite solo 1 descarga simultánea )"
#, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr ""
+msgstr "¡Entrada no válida! Intente nuevamente con una entrada válida [1 a {max_downloads}, o 0 para deshabilitar]"
msgid "Parallel Downloads"
-msgstr ""
+msgstr "Descargas paralelas"
-#, fuzzy
msgid "ESC to skip"
-msgstr "Use ESC para saltar"
+msgstr "ESC para omitir"
msgid "CTRL+C to reset"
-msgstr ""
+msgstr "CTRL+C para restablecer"
msgid "TAB to select"
-msgstr ""
+msgstr "TAB para seleccionar"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[Valor predeterminado: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Para poder usar esta traducción, instale manualmente una fuente que admita el idioma."
msgid "The font should be stored as {}"
-msgstr ""
+msgstr "La fuente debe almacenarse como {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall requiere privilegios de root para ejecutarse. Consulte --help para más información."
+
+msgid "Select an execution mode"
+msgstr "Seleccione un modo de ejecución"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "No se puede recuperar el perfil de la URL especificada: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Los perfiles deben tener un nombre único, pero se encontraron definiciones de perfil con nombre duplicado: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Seleccione uno o más dispositivos para usar y configurar"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Si restablece la selección del dispositivo, esto también restablecerá el diseño actual del disco. ¿Está seguro?"
+
+msgid "Existing Partitions"
+msgstr "Particiones existentes"
+
+msgid "Select a partitioning option"
+msgstr "Seleccione una opción de partición"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Ingrese el directorio raíz de los dispositivos montados: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Capacidad mínima para la partición /home: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Capacidad mínima para la partición 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 "Esta es una lista de profiles_bck preprogramados que podrían facilitar la instalación de cosas como entornos de escritorio"
+
+msgid "Current profile selection"
+msgstr "Selección de perfil actual"
+
+msgid "Remove all newly added partitions"
+msgstr "Eliminar todas las particiones recién agregadas"
+
+msgid "Assign mountpoint"
+msgstr "Asignar punto de montaje"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Marcar/Desmarcar para formatear (borra datos)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Marcar/Desmarcar como arrancable"
+
+msgid "Change filesystem"
+msgstr "Cambiar el sistema de archivos"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Marcar/Desmarcar como comprimido"
+
+msgid "Set subvolumes"
+msgstr "Establecer subvolúmenes"
+
+msgid "Delete partition"
+msgstr "Eliminar partición"
+
+msgid "Partition"
+msgstr "Partición"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Esta partición está actualmente cifrada, para formatearla se debe especificar un sistema de archivos"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Los puntos de montaje de la partición son relativos al interior de la instalación; el arranque sería /boot como ejemplo."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Si se establece el punto de montaje /boot, la partición también se marcará como arrancable."
+
+msgid "Mountpoint: "
+msgstr "Punto de montaje: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Sectores libres actuales en el dispositivo {}:"
+
+msgid "Total sectors: {}"
+msgstr "Sectores totales: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Introduzca el sector de inicio (predeterminado: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Ingrese el sector final de la partición (porcentaje o número de bloque, predeterminado: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Esto eliminará todas las particiones recientemente agregadas, ¿continuar?"
+
+msgid "Partition management: {}"
+msgstr "Gestión de particiones: {}"
+
+msgid "Total length: {}"
+msgstr "Largo total: {}"
+
msgid "Encryption type"
-msgstr "Contraseña de cifrado"
+msgstr "Tipo de cifrado"
msgid "Partitions"
-msgstr ""
+msgstr "Particiones"
msgid "No HSM devices available"
-msgstr ""
+msgstr "No hay dispositivos HSM disponibles"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Seleccione qué particiones cifrar"
+msgstr "Particiones a cifrar"
-#, fuzzy
msgid "Select disk encryption option"
-msgstr "Seleccione el diseño del disco"
+msgstr "Seleccione la opción de cifrado de disco"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Seleccione un dispositivo FIDO2 para usar con HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Utlizar un diseño de partición predeterminado de mejor esfuerzo"
+
+msgid "Manual Partitioning"
+msgstr "Partición manual"
+
+msgid "Pre-mounted configuration"
+msgstr "Configuración premontada"
+
+msgid "Unknown"
+msgstr "Desconocido"
+
+msgid "Partition encryption"
+msgstr "Cifrado de partición"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formateando {} en "
+
+msgid "↠Back"
+msgstr "↠Regresar"
+
+msgid "Disk encryption"
+msgstr "Cifrado de disco"
+
+msgid "Configuration"
+msgstr "Configuration"
+
+msgid "Password"
+msgstr "Contraseña"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} contiene particiones en cola, esto eliminará esas particiones, ¿estás seguro?"
+msgstr "Todos los ajustes se restablecerán, ¿estás seguro?"
msgid "Back"
+msgstr "Regresar"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Por favor, elija qué saludador instalar para los perfiles elegidos: {}"
+
+msgid "Environment type: {}"
+msgstr "Tipo de entorno: {}"
+
+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 "El controlador propietario de Nvidia no es compatible con Sway. Es probable que encuentre problemas, ¿Desear continuar?"
+
+msgid "Installed packages"
+msgstr "Paquetes instalados"
+
+msgid "Add profile"
+msgstr "Agregar perfil"
+
+msgid "Edit profile"
+msgstr "Editar perfil"
+
+msgid "Delete profile"
+msgstr "Eliminar perfil"
+
+msgid "Profile name: "
+msgstr "Nombre de perfil: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "El nombre de perfil que ingresó ya está en uso. Intente nuevamente"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Paquetes que se instalarán con este perfil (separados por espacios, déjelo en blanco para omitir): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Servicios que se habilitarán con este perfil (separados por espacios, deje en blanco para omitir): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "¿Debería habilitarse este perfil para la instalación?"
+
+msgid "Create your own"
+msgstr "Crea tu propio"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"Seleccione un controlador de gráficos o déjelo en blanco para instalar todos los controladores de código abierto"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway necesita acceso a sus dispositivos de hardware (teclado, mouse, etc.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Elija una opción para darle a Sway acceso a su hardware"
+
+msgid "Graphics driver"
+msgstr "Controlador de gráficos"
+
+msgid "Greeter"
+msgstr "Saludador"
+
+msgid "Please chose which greeter to install"
+msgstr "Por favor, elija qué saludador instalar"
#, fuzzy
-msgid "Password"
-msgstr "Contraseña de root"
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Esta es una lista de default_profiles preprogramados"
-msgid "Partition encryption"
+msgid "Disk configuration"
+msgstr "Configuración del disco"
+
+msgid "Profiles"
+msgstr "Perfiles"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Encontrar posibles directorios para guardar archivos de configuración..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Seleccione el directorio (o directorios) para guardar los archivos de configuración"
+
+msgid "Add a custom mirror"
+msgstr "Agregar un espejo personalizado"
+
+msgid "Change custom mirror"
+msgstr "Cambiar espejo personalizado"
+
+msgid "Delete custom mirror"
+msgstr "Eliminar espejo personalizado"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Ingrese el nombre (deje en blanco para omitir): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Ingrese la URL (deje en blanco para omitir): "
+
+msgid "Select signature check option"
+msgstr "Seleccione la opción de verificación de firma"
+
+msgid "Select signature option"
+msgstr "Seleccione la opción de firma"
+
+msgid "Custom mirrors"
+msgstr "Espejos personalizados"
+
+msgid "Defined"
+msgstr "Definido"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Guardar la configuración del usuario (incluido el diseño del disco)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Ingrese un directorio para guardar las configuraciones (completar con tabulación habilitado)\n"
+"Guardar directorio: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"¿Desea guardar {} archivo(s) de configuración en la siguiente ubicación?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Guardar {} archivos de configuración en {}"
+
+msgid "Mirrors"
+msgstr "Espejos"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "Regiones espejo"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Valor máximo : {} ( Habilita {} descargas paralelas, permite {max_downloads+1} descargas simultáneas )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "¡Entrada no válida! Intente nuevamente con una entrada válida [1 a {}, o 0 para deshabilitar]"
+
+msgid "Locales"
+msgstr ""
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Total: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Todos los valores ingresados pueden tener una unidad como sufijo: B, KB, KiB, MB, MiB ..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Si no se proporciona ninguna unidad, el valor se interpreta como sectores"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Ingrese el inicio (predeterminado: sector {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Ingrese el final (predeterminado: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "No se pueden determinar los dispositivos fido2. ¿Está instalado libfido2?"
+
+msgid "Path"
+msgstr "Ruta"
+
+msgid "Manufacturer"
+msgstr "Fabricante"
+
+msgid "Product"
+msgstr "Producto"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configuración no válida: {error}"
+
+msgid "Type"
+msgstr "Tipo"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Esta opción habilita la cantidad de descargas paralelas que pueden ocurrir durante las descargas de paquetes"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
msgstr ""
+"Ingrese el número de descargas paralelas que se habilitarán.\n"
+"\n"
+"Nota:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Valor máximo recomendado : {} ( Permite {} descargas paralelas simultáneas )"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): "
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Deshabilitar/Predeterminado : 0 ( Deshabilita la descarga paralela, permite solo 1 descarga simultánea )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "¡Entrada no válida! Intente nuevamente con una entrada válida [o 0 para deshabilitar]"
+
+#, fuzzy
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland necesita acceso a su asiento (colección de dispositivos de hardware, es decir, teclado, mouse, etc.)"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Ingrese el sector final de la partición (porcentaje o número de bloque, ej: {}): "
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Elija una opción para darle acceso a Hyprland a su hardware"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Todos los valores introducidos pueden tener como sufijo una unidad: %, B, KB, KiB, MB, MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "¿Le gustaría utilizar imágenes del kernel unificadas?"
+
+msgid "Unified kernel images"
+msgstr "Imágenes del kernel unificadas"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Esperando a que se complete la sincronización de la hora (timedatectl show)."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "La sincronización de hora no se completa mientras espera - consulte los documentos para encontrar soluciones: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Saltarse la espera de sincronización automática de la hora (esto puede causar problemas si la hora no está sincronizada durante la instalación)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Esperando a que se complete la sincronización del llavero de Arch Linux (archlinux-keyring-wkd-sync)."
+
+msgid "Selected profiles: "
+msgstr "Perfiles seleccionados: "
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "La sincronización de hora no se completa mientras espera - consulte los documentos para encontrar soluciones: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Marcar/Desmarcar como arrancable"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "¿Te gustaría usar la compresión BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
#~ msgid "Add :"
#~ msgstr "Añadir :"
diff --git a/archinstall/locales/et/LC_MESSAGES/base.mo b/archinstall/locales/et/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..24e91a77
--- /dev/null
+++ b/archinstall/locales/et/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po
new file mode 100644
index 00000000..5bc11081
--- /dev/null
+++ b/archinstall/locales/et/LC_MESSAGES/base.po
@@ -0,0 +1,1277 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.3\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] Siia on loodud logifail: {} {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " Esitage see probleem (ja fail) aadressil https://github.com/archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "Kas sa tahad katkestada?"
+
+msgid "And one more time for verification: "
+msgstr "Ja veel kord kinnitamiseks: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "Kas soovite zramis kasutada swapi?"
+
+msgid "Desired hostname for the installation: "
+msgstr "Installimiseks soovitud hostinimi: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "Kasutajanimi nõutud superkasutajale sudo õigustega: "
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "Lisa kasutajate lisamine(jäta tühjaks kui ei soovi lisa kasutajaid): "
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "Kas see kasutaja peaks olema superkasutaja (sudo õigustega)"
+
+msgid "Select a timezone"
+msgstr "Vali ajatsoon"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "Kas soovite systemd-booti asemel kasutada GRUBi buudilaadijana?"
+
+msgid "Choose a bootloader"
+msgstr "Vali buudilaadija"
+
+msgid "Choose an audio server"
+msgstr "Vali audio server"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "Paigaldatakse ainult sellised paketid nagu base, base-devel, linux, linux-firmware, efibootmgr ja valikulised profiilipaketid."
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr "Kui soovite veebibrauserit, näiteks firefox või chromium, saate selle määrata järgmises viipas."
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "Kirjutage paigaldatavad lisapaketid (tühikutega eraldatuna, jätke tühjaks, kui soovite vahele jätta): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr "ISO-võrgu konfiguratsiooni kopeerimine paigaldusse"
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)."
+
+msgid "Select one network interface to configure"
+msgstr "Valige üks võrguliides konfigureerimiseks"
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr "Valige, millist režiimi soovite konfigureerida \"{}\" või jätke vahele, kasutada vaikimisi režiimi \"{}\""
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "Sisestage IP ja alamvõrk {} (näide: 192.168.0.5/24): "
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "Sisestage oma võrguvärava (ruuteri) IP-aadress või jätke tühjaks, kui see puudub: "
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr "Sisestage oma DNS-serverid (tühikuga eraldatud, jätke tühjaks, kui neid ei ole): "
+
+msgid "Select which filesystem your main partition should use"
+msgstr "Valige, millist failisüsteemi teie peamine partitsioon peaks kasutama"
+
+msgid "Current partition layout"
+msgstr "Praegune partitsiooni paigutus"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"Valige, mida teha\n"
+"{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "Sisestage partitsiooni jaoks soovitud failisüsteemi tüüp"
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "Sisestage alguskoht (parted ühikutes: s, GB, % jne; vaikimisi: {}): "
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "Sisestage lõpu asukoht (parted ühikutes: s, GB, % jne; näiteks: {}): "
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "{} sisaldab järjekorras olevaid partitsioone, see eemaldab need, kas olete kindel?"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}\n"
+"\n"
+"\n"
+"Valige indeksi järgi, milliseid partitsioone kustutada"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}\n"
+"\n"
+"\n"
+"Valige indeksi järgi, millise partitsiooni kuhu paigaldada"
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Partitsioonide kinnituspunktid on suhtelised installeerimise sees, näiteks boot oleks /boot."
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr "Valige, kuhu partitsiooni paigaldada (jätke tühjaks, et eemaldada paigalduspunkt): "
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"{}\n"
+"\n"
+"Valige, millist partitsiooni soovite vormindamiseks maskeerida"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"{}\n"
+"\n"
+"\n"
+"Valige, millist partitsiooni soovite krüpteerituks märkida"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"{}\n"
+"\n"
+"\n"
+"Valige, millist partitsiooni soovite märkida käivitatavaks"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"{}\n"
+"\n"
+"\n"
+"Valige, millisele partitsioonile failisüsteemi määrata"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "Sisestage partitsiooni jaoks soovitud failisüsteemi tüüp: "
+
+msgid "Archinstall language"
+msgstr "Archinstalli keel"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr "Pühkige kõik valitud kettad ja kasutage parimat võimalikku vaikimisi partitsiooni paigutust"
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr "Valige, mida teha iga üksiku kettaga (järgneb partitsiooni kasutamine)"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "Valige, mida soovite valitud plokkseadmetega teha"
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr "See on nimekiri eelprogrammeeritud profiilidest, need võivad lihtsustada selliste asjade nagu töölauakeskkondade paigaldamist"
+
+msgid "Select keyboard layout"
+msgstr "Valige klaviatuuri paigutus"
+
+msgid "Select one of the regions to download packages from"
+msgstr "Valige üks piirkondadest, kust pakette alla laadida"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "Valige üks või mitu kõvaketast kasutamiseks ja konfigureerimiseks"
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr "Parima ühilduvuse saavutamiseks oma AMD riistvaraga võiksite kasutada kas kõiki avatud lähtekoodiga või AMD/ATI valikuid."
+
+msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
+msgstr ""
+"Parima ühilduvuse saavutamiseks oma Inteli riistvaraga võiksite kasutada kas kõiki avatud lähtekoodiga või Inteli valikuid.\n"
+"\n"
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr ""
+"Parima ühilduvuse saavutamiseks oma Nvidia riistvaraga peaksite kasutama Nvidia enda toodetud draiverit.\n"
+"\n"
+"\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Valige graafikadraiver või jätke tühjaks, et paigaldada kõik avatud lähtekoodiga draiverid"
+
+msgid "All open-source (default)"
+msgstr "Kõik avatud lähtekoodiga (vaikimisi)"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "Valige, milliseid tuumasid soovite kasutada või jätke vaikimisi tühjaks \"{}\""
+
+msgid "Choose which locale language to use"
+msgstr "Valige, millist asukoha keelt kasutada"
+
+msgid "Choose which locale encoding to use"
+msgstr "Valige, millist asukoha kodeeringut kasutada"
+
+msgid "Select one of the values shown below: "
+msgstr "Valige üks allpool esitatud väärtustest: "
+
+msgid "Select one or more of the options below: "
+msgstr "Valige üks või mitu järgmistest võimalustest: "
+
+msgid "Adding partition...."
+msgstr "Partitsiooni lisamine...."
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr "Partitsiooni lisamine...."
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "Viga: URL-i \"{}\" profiilide loetlemine andis tulemuseks:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "Viga: {}\" tulemust JSON-ina dekodeerida:"
+
+msgid "Keyboard layout"
+msgstr "Klaviatuuri paigutus"
+
+msgid "Mirror region"
+msgstr "Peegelregioon"
+
+msgid "Locale language"
+msgstr "Kohalik keel"
+
+msgid "Locale encoding"
+msgstr "Kohaliku keele kodeering"
+
+msgid "Drive(s)"
+msgstr "Draiv(id)"
+
+msgid "Disk layout"
+msgstr "Ketta paigutus"
+
+msgid "Encryption password"
+msgstr "Krüpteerimise parool"
+
+msgid "Swap"
+msgstr "Swap"
+
+msgid "Bootloader"
+msgstr "Buudilaadija"
+
+msgid "Root password"
+msgstr "Juur parool"
+
+msgid "Superuser account"
+msgstr "Superkasutaja konto"
+
+msgid "User account"
+msgstr "Kasutaja konto"
+
+msgid "Profile"
+msgstr "Profiil"
+
+msgid "Audio"
+msgstr "Audio"
+
+msgid "Kernels"
+msgstr "Kernelid"
+
+msgid "Additional packages"
+msgstr "Täiendavad paketid"
+
+msgid "Network configuration"
+msgstr "Võrgu konfiguratsioon"
+
+msgid "Automatic time sync (NTP)"
+msgstr "Automaatne ajasünkroonimine"
+
+msgid "Install ({} config(s) missing)"
+msgstr "Install ({} config(id) puudu(vad)"
+
+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 ""
+"Sa otsustasid kõvaketta valiku vahele jätta\n"
+"ja kasutad mis tahes draivi seadistust, mis on paigaldatud {} (eksperimentaalne)\n"
+"HOIATUS: Archinstall ei kontrolli selle seadistuse sobivust\n"
+"Kas soovite jätkata?"
+
+msgid "Re-using partition instance: {}"
+msgstr "Partitsiooni instantsi taaskasutamine: {}"
+
+msgid "Create a new partition"
+msgstr "Uue partitsiooni loomine"
+
+msgid "Delete a partition"
+msgstr "Kustuta paritsioon"
+
+msgid "Clear/Delete all partitions"
+msgstr "Puhasta/Kustuta kõik partitsioonid"
+
+msgid "Assign mount-point for a partition"
+msgstr "Määrake partitsioonile kinnituspunkt"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "Märgistage/mittemärgistage vormindatav partitsioon (kustutab andmed)"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "Märgistage/mittemärgistage partitsiooni krüpteerituks"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "Märgistage/mittemärgistage partitsiooni käivitatavaks (automaatne /boot jaoks)"
+
+msgid "Set desired filesystem for a partition"
+msgstr "Soovitud failisüsteemi määramine partitsiooni jaoks"
+
+msgid "Abort"
+msgstr "Katkesta"
+
+msgid "Hostname"
+msgstr "Hostinimi"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "Ei ole konfigureeritud, ei ole saadaval, kui seda ei ole käsitsi seadistatud"
+
+msgid "Timezone"
+msgstr "Ajatsoon"
+
+msgid "Set/Modify the below options"
+msgstr "Määrake/muutke järgmisi valikuid"
+
+msgid "Install"
+msgstr "Install"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"Kasutage ESC vahelejätmiseks\n"
+"\n"
+"\n"
+"\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "Soovitage partitsiooni paigutust"
+
+msgid "Enter a password: "
+msgstr "Sisestage parool: "
+
+msgid "Enter a encryption password for {}"
+msgstr "Sisestage {}'le krüpteerimis parool"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "Sisestage ketta krüpteerimise parool (krüpteerimise puudumisel jätke see tühjaks): "
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "Looge nõutav sudo õigustega superkasutaja: "
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "Sisestage juur parool (jätke tühjaks, et keelata root): "
+
+msgid "Password for user \"{}\": "
+msgstr "Kasutaja \"{}\" parool: "
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr "Täiendavate pakettide olemasolu kontrollimine (see võib võtta paar sekundit)"
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr "Kas soovite kasutada automaatset ajasünkroniseerimist (NTP) vaikimisi ajaserveritega?\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 ""
+"NTP toimimiseks võib olla vaja riistvara aega ja muid konfiguratsioonijärgseid samme.\n"
+"Lisateavet leiate Archi wikist"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "Sisestage kasutajanimi, et luua lisakasutaja (jätke tühjaks, et vahele jätta): "
+
+msgid "Use ESC to skip\n"
+msgstr "Kasutage ESC'i et vahele jätta\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+"\n"
+" Valige loetelust objekt ja valige selle täitmiseks üks olemasolevatest toimingutest"
+
+msgid "Cancel"
+msgstr "Tühista"
+
+msgid "Confirm and exit"
+msgstr "Kinnita ja lahku"
+
+msgid "Add"
+msgstr "Lisa"
+
+msgid "Copy"
+msgstr "Kopeeri"
+
+msgid "Edit"
+msgstr "Muuda"
+
+msgid "Delete"
+msgstr "Kustuta"
+
+msgid "Select an action for '{}'"
+msgstr "Valige tegevus '{}' jaoks"
+
+msgid "Copy to new key:"
+msgstr "Kopeeri uude võtmesse:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "Tundmatu nic-tüüp: {}. Võimalikud väärtused on {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"See on teie valitud konfiguratsioon:"
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "Pacman on juba käivitatud ja ootab maksimaalselt 10 minutit, kuni see lõpeb."
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr "Eelnev pacmani lukk ei ole kunagi väljunud. Enne archinstall'i kasutamist puhastage olemasolevad pacmani sessioonid."
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "Valige, milliseid valikulisi lisa repositooriumeid soovite lubada"
+
+msgid "Add a user"
+msgstr "Lisa kasutaja"
+
+msgid "Change password"
+msgstr "Muuda parool"
+
+msgid "Promote/Demote user"
+msgstr "Edenda/alanda kasutajat"
+
+msgid "Delete User"
+msgstr "Kustuta kasutaja"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"Uue kasutaja määramine\n"
+
+msgid "User Name : "
+msgstr "Kasutajanimi : "
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "Kas {} peaks olema superkasutaja (sudoer)?"
+
+msgid "Define users with sudo privilege: "
+msgstr "Määrake sudo privileegiga kasutajad: "
+
+msgid "No network configuration"
+msgstr "Puudub võrgu konfiguratsioon"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "Määrake soovitud alamköited btrfs-i partitsioonile"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}\n"
+"\n"
+"Valige, millisele partitsioonile alamkogumid määrata"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "Hallake praeguse partitsiooni btrfs-i alamköiteid"
+
+msgid "No configuration"
+msgstr "Puudub konfiguratsioon"
+
+msgid "Save user configuration"
+msgstr "Salvesta kasutaja konfiguratsioon"
+
+msgid "Save user credentials"
+msgstr "Salvesta kasutaja volitused"
+
+msgid "Save disk layout"
+msgstr "Salvesta ketta paigutus"
+
+msgid "Save all"
+msgstr "Salvesta kõik"
+
+msgid "Choose which configuration to save"
+msgstr "Valige milline konfiguratsioon salvestada"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "Sisestage salvestatava(te) konfiguratsiooni(de) kataloog: "
+
+msgid "Not a valid directory: {}"
+msgstr "Ei ole sobiv kataloog: {}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "Teie kasutatav salasõna näib olevat nõrk,"
+
+msgid "are you sure you want to use it?"
+msgstr "oled sa kindel et soovid seda kasutada?"
+
+msgid "Optional repositories"
+msgstr "Valikulised repositooriumid"
+
+msgid "Save configuration"
+msgstr "Salvesta konfiguratsioon"
+
+msgid "Missing configurations:\n"
+msgstr "Puuduvad konfiguratsioonid:\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "Tuleb määrata kas juur-parool või vähemalt 1 superkasutaja"
+
+msgid "Manage superuser accounts: "
+msgstr "Hallake superkasutaja kontosi "
+
+msgid "Manage ordinary user accounts: "
+msgstr "Hallake tavalisi kasutajaid: "
+
+msgid " Subvolume :{:16}"
+msgstr " Alamköide :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " paigaldatud {:16}"
+
+msgid " with option {}"
+msgstr " koos valikuga {}"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+" Täida uue alamhulga soovitud väärtused \n"
+
+msgid "Subvolume name "
+msgstr "Alamhulga nimi "
+
+msgid "Subvolume mountpoint"
+msgstr "Alamhulga kinnituspunkt"
+
+msgid "Subvolume options"
+msgstr "Alamhulga valikud"
+
+msgid "Save"
+msgstr "Salvesta"
+
+msgid "Subvolume name :"
+msgstr "Alamhulga nimi:"
+
+msgid "Select a mount point :"
+msgstr "Valige kinnitus punkt :"
+
+msgid "Select the desired subvolume options "
+msgstr "Valige soovitud alamhulga valikud "
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "Määrake sudo õigustega kasutajad kasutajanime järgi: "
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] Siin on loodud logifail: {}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "Kas soovite kasutada BTRFS-i alamkogusid vaikimisi struktuuriga?"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "Kas soovite kasutada BTRFS-i tihendamist?"
+
+msgid "Would you like to create a separate partition for /home?"
+msgstr "Kas soovite luua eraldi partitsiooni /home jaoks?"
+
+msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
+msgstr "Valitud ketastel ei ole automaatseks soovituseks vajalikku minimaalset mahtu\n"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "Minimaalne mahutavus /home partitsioonile: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "Arch Linuxi partitsiooni minimaalne mahutavus: {}GB"
+
+msgid "Continue"
+msgstr "Jätka"
+
+msgid "yes"
+msgstr "jah"
+
+msgid "no"
+msgstr "ei"
+
+msgid "set: {}"
+msgstr "seadista: {}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "Käsitsi konfiguratsiooniseade peab olema loend"
+
+msgid "No iface specified for manual configuration"
+msgstr "Käsitsi seadistamiseks pole iface'i määratud"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr "Manuaalne nici konfigureerimine ilma automaatse DHCPta nõuab IP-aadressi"
+
+msgid "Add interface"
+msgstr "Lisa liides"
+
+msgid "Edit interface"
+msgstr "Muuda liidest"
+
+msgid "Delete interface"
+msgstr "Kustutage liides"
+
+msgid "Select interface to add"
+msgstr "Valige millist liidest lisada"
+
+msgid "Manual configuration"
+msgstr "Manuaalne konfiguratsioon"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "Märgistada/mittemärgistada partitsiooni kompressiooniks (ainult btrfs)"
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "Teie kasutatav salasõna tundub olevat nõrk, kas olete kindel, et soovite seda kasutada?"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr "Pakub valikut töölauakeskkondi ja plaadistatavaid aknahaldureid, nt gnome, kde, sway"
+
+msgid "Select your desired desktop environment"
+msgstr "Valige soovitud töölauakeskkonda"
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr "Väga lihtne paigaldus, mis võimaldab teil Arch Linuxi kohandada vastavalt oma äranägemisele."
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr "Annab valiku erinevate serveripakettide paigaldamiseks ja aktiveerimiseks, nt httpd, nginx, mariadb"
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr "Valige, milliseid servereid paigaldada, kui ühtegi ei ole, siis tehakse minimaalne paigaldus"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr "Installeerib minimaalse süsteemi ning xorgi ja graafikadraiverid."
+
+msgid "Press Enter to continue."
+msgstr "Jätkamiseks vajutage Enter."
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr "Kas soovite chrootida äsja loodud installatsiooni ja teostada installeerimisjärgset konfigureerimist?"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "Kas olete kindel, et soovite seda seadistust lähtestada?"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "Valige üks või mitu kasutatavat kõvaketast ja konfigureerige\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "Olemasoleva seadistuse muutmine lähtestab ketta paigutuse!"
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr "Kui lähtestate kõvaketta valiku, lähtestab see ka praeguse kettapaigutuse. Oled sa kindel?"
+
+msgid "Save and exit"
+msgstr "Salvesta ja lahku"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"{}\n"
+"sisaldab järjekorras olevaid partitsioone, see eemaldab need, oled sa kindel?"
+
+msgid "No audio server"
+msgstr "Puudub audio server"
+
+msgid "(default)"
+msgstr "(vaikimisi)"
+
+msgid "Use ESC to skip"
+msgstr "Kasutage ESC vahelejätmiseks"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+"Kasuta CTRL+C praeguse valiku lähtestamiseks\n"
+"\n"
+
+msgid "Copy to: "
+msgstr "Kopeeri: "
+
+msgid "Edit: "
+msgstr "Muuda: "
+
+msgid "Key: "
+msgstr "Võti: "
+
+msgid "Edit {}: "
+msgstr "Muuda {}: "
+
+msgid "Add: "
+msgstr "Lisa: "
+
+msgid "Value: "
+msgstr "Väärtus: "
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr "Saad vahele jätta ketta valimise ja partitsioneerimise ning kasutada mis tahes draivi-komplekti, mis on paigaldatud /mnt (eksperimentaalne)."
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "Valige üks ketastest või jätke vahele ja kasutage vaikimisi /mnt"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "Valige, milliseid partitsioone soovite vormindamiseks märkida:"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "Kasutage HSM-i krüpteeritud draivi avamiseks"
+
+msgid "Device"
+msgstr "Seade"
+
+msgid "Size"
+msgstr "Suurus"
+
+msgid "Free space"
+msgstr "Vaba mälu"
+
+msgid "Bus-type"
+msgstr "Bussi tüüp"
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr "Tuleb määrata kas root-sõna või vähemalt 1 kasutaja, kellel on sudo õigused"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "Sisestage kasutajanimi (jätke tühjaks, et vahele jätta): "
+
+msgid "The username you entered is invalid. Try again"
+msgstr "Teie sisestatud kasutajanimi ei sobi. Proovige uuesti"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "Kas \"{}\" peaks olema superkasutaja (sudo)?"
+
+msgid "Select which partitions to encrypt"
+msgstr "Valige, milliseid partitsioone krüpteerida"
+
+msgid "very weak"
+msgstr "väga nõrk"
+
+msgid "weak"
+msgstr "nõrk"
+
+msgid "moderate"
+msgstr "keskmine"
+
+msgid "strong"
+msgstr "tugev"
+
+msgid "Add subvolume"
+msgstr "Lisage alamhulk"
+
+msgid "Edit subvolume"
+msgstr "Osahulga redigeerimine"
+
+msgid "Delete subvolume"
+msgstr "Kustuta osahulk"
+
+msgid "Configured {} interfaces"
+msgstr "Konfigureeritud {} liidesed"
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr "See valik võimaldab installimise ajal valida paralleelsete allalaadimiste arvu"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"Sisestage lubatavate paralleelsete allalaadimiste arv.\n"
+" (Sisestage väärtus vahemikus 1 kuni {max_downloads})\n"
+"note:"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )"
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr " - Keela/Vaikimisi : 0 ( keelab paralleelse allalaadimise, võimaldab ainult 1 allalaadimist korraga )"
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]."
+
+msgid "Parallel Downloads"
+msgstr "Paralleelsed allalaadimised"
+
+msgid "ESC to skip"
+msgstr "ESC vahelejätmiseks"
+
+msgid "CTRL+C to reset"
+msgstr "CTRL+C lähtestamiseks"
+
+msgid "TAB to select"
+msgstr "TAB valimiseks"
+
+msgid "[Default value: 0] > "
+msgstr "[Vaikimisi väärtus: 0] > "
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr "Selleks, et seda tõlget kasutada, paigaldage käsitsi fondi, mis toetab seda keelt."
+
+msgid "The font should be stored as {}"
+msgstr "Font tuleks salvestada kujul {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall vajab käivitamiseks juurkasutaja õigusi. Vaata --help."
+
+msgid "Select an execution mode"
+msgstr "Valige täitmisrežiim"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Ei õnnestu profiili hankida määratud url'ist: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Profiilidel peab olema unikaalne nimi, kuid leiti topeltnimega profiilide määratlusi: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Valige üks või mitu seadet mida kasutada ja konfigureerida"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Kui lähtestate seadme valiku, siis lähtestate sellega ka praeguse draivi paigutuse. Kas olete kindel?"
+
+msgid "Existing Partitions"
+msgstr "Olemasolevad Jaotused"
+
+msgid "Select a partitioning option"
+msgstr "Valige jaotuste valik"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Sisestage paigaldatud seadmete juurkataloog: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Minimaalne mahutavus /home partitsioonile: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Arch Linuxi partitsiooni minimaalne mahutavus: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "See on nimekiri eelprogrammeeritud profiilidest_bck, need võivad lihtsustada selliste asjade nagu töölauakeskkondade paigaldamist"
+
+msgid "Current profile selection"
+msgstr "Praegune profiilivalik"
+
+msgid "Remove all newly added partitions"
+msgstr "Eemaldage kõik äsja lisatud partitsioonid"
+
+msgid "Assign mountpoint"
+msgstr "Määra paigaldamis punkt"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Märgistada/mittemärgistada vormindamiseks (kustutab andmed)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Märgistada/mittemärgistada käivitatavak"
+
+msgid "Change filesystem"
+msgstr "Failisüsteemi muutmine"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Märgistada/mittemärgistada kui tihendatud"
+
+msgid "Set subvolumes"
+msgstr "Alamköite määramine"
+
+msgid "Delete partition"
+msgstr "Kustuta partitsioon"
+
+msgid "Partition"
+msgstr "Partitsioon"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "See partitsioon on praegu krüpteeritud, selle vormindamiseks tuleb määrata failisüsteem"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Partitsiooni kinnituspunktid on suhtelised installeerimise sees, näiteks boot oleks /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Kui paigalduspunkt /boot on määratud, siis märgitakse partitsioon ka käivitatavaks."
+
+msgid "Mountpoint: "
+msgstr "Paigalduspunkt: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Praegused vabad sektorid seadmes {}:"
+
+msgid "Total sectors: {}"
+msgstr "Sektorid kokku: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Sisestage algussektor (vaikimisi: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Sisestage partitsiooni lõppsektor (protsent või plokkide number, vaikimisi: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "See eemaldab kõik äsja lisatud partitsioonid, jätka?"
+
+msgid "Partition management: {}"
+msgstr "Partitsiooni haldamine:{}"
+
+msgid "Total length: {}"
+msgstr "Kogupikkus: {}"
+
+msgid "Encryption type"
+msgstr "Krüpteerimise tüüp"
+
+msgid "Partitions"
+msgstr "Partitsioonid"
+
+msgid "No HSM devices available"
+msgstr "Puuduvad HSM seadmed"
+
+msgid "Partitions to be encrypted"
+msgstr "Krüpteeritavad partitsioonid"
+
+msgid "Select disk encryption option"
+msgstr "Valige ketta krüpteerimise valik"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "Valige HSM-i jaoks kasutatav FIDO2-seade"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Kasutage parimat võimalikku vaikimisi partitsiooni paigutust"
+
+msgid "Manual Partitioning"
+msgstr "Käsitsi partitsioneerimine"
+
+msgid "Pre-mounted configuration"
+msgstr "Eelnevalt paigaldatud konfiguratsioon"
+
+msgid "Unknown"
+msgstr "Tundmatu"
+
+msgid "Partition encryption"
+msgstr "Partitsiooni krüpteerimine"
+
+msgid " ! Formatting {} in "
+msgstr " ! Vormindamine {} "
+
+msgid "↠Back"
+msgstr "Tagasi"
+
+msgid "Disk encryption"
+msgstr "Ketta krüpteerimine"
+
+msgid "Configuration"
+msgstr "Konfiguratsioon"
+
+msgid "Password"
+msgstr "Parool"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "Kõik seaded lähtestatakse, kas sa oled kindel?"
+
+msgid "Back"
+msgstr "Tagasi"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Palun valige millist tervitajat installida valitud profiilidele"
+
+msgid "Environment type: {}"
+msgstr "Keskkonna tüüp: {}"
+
+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 "Sway ei toeta Nvidia enda draiverit. Tõenäoliselt tekib teil probleeme, kas teile sobib see?"
+
+msgid "Installed packages"
+msgstr "Installeeritud paketid"
+
+msgid "Add profile"
+msgstr "Lisa profiil"
+
+msgid "Edit profile"
+msgstr "Muuda profiili"
+
+msgid "Delete profile"
+msgstr "Kustuta profiil"
+
+msgid "Profile name: "
+msgstr "Profiili nimi: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Sisestatud profiili nimi on juba kasutuses. Proovi uuesti"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Selle profiiliga paigaldatavad paketid (tühikuga eraldatud, jäta tühjaks, et jätta vahele): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Selle profiiliga lubatavad teenused (tühikuga eraldatud, jäta tühjaks, kui soovid vahele jätta): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Kas see profiil peaks olema paigaldamiseks lubatud?"
+
+msgid "Create your own"
+msgstr "Loo oma"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"Vali graafikadraiver või jäta tühjaks, et paigaldada kõik avatud lähtekoodiga draiverid"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway vajab juurdepääsu teie seatile (riistvaraseadmete kogum, st klaviatuur, hiir jne."
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Valige valik, et anda Sway'le juurdepääs teie riistvarale"
+
+msgid "Graphics driver"
+msgstr "Graafika draiver"
+
+msgid "Greeter"
+msgstr "Tervitaja"
+
+msgid "Please chose which greeter to install"
+msgstr "Palun valige millist tervitajat installida"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "See on eelnevalt programmeeritud vaikimisi_profiilide nimekiri"
+
+msgid "Disk configuration"
+msgstr "Ketta konfiguratsioon"
+
+msgid "Profiles"
+msgstr "Profiilid"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Võimalike kataloogide leidmine konfiguratsioonifailide salvestamiseks ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Konfigureerimisfailide salvestamise kataloogi (või kataloogide) valimine"
+
+msgid "Add a custom mirror"
+msgstr "Lisa kohandatud peegel"
+
+msgid "Change custom mirror"
+msgstr "Muuda kohandatud peeglit"
+
+msgid "Delete custom mirror"
+msgstr "Kustuta kohandadtud peeglit"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Sisesta nimi (jätke tühjaks, et vahele jätta): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Sisesta url (jätke tühjaks, et vahele jätta): "
+
+msgid "Select signature check option"
+msgstr "Valige allkirja kontrollimise võimalus"
+
+msgid "Select signature option"
+msgstr "Valige allkirja valik"
+
+msgid "Custom mirrors"
+msgstr "Kohandatud peegel"
+
+msgid "Defined"
+msgstr "Defineeritud"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Salvesta kasutaja konfiguratsioon (kaasa arvatud plaadi paigutus)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Sisestage salvestatava(te) konfiguratsiooni(de) kataloog (vahekaartide täitmine lubatud)\n"
+"Salvesta kataloog: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Kas soovite salvestada {} konfiguratsioonifaili(d) järgmisesse asukohta?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "{} konfiguratsioonifailide salvestamine {}"
+
+msgid "Mirrors"
+msgstr "Peeglid"
+
+msgid "Mirror regions"
+msgstr "Peegel regioonid"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Maksimaalne väärtus : {max_downloads} ( Võimaldab {max_downloads} paralleelset allalaadimist, lubab {max_downloads+1} allalaadimist korraga )"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]."
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)."
+
+#, fuzzy
+msgid "Total: {} / {}"
+msgstr "Kogupikkus: {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Sisestage algussektor (vaikimisi: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Sisestage algussektor (vaikimisi: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Manuaalne konfiguratsioon"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "See valik võimaldab installimise ajal valida paralleelsete allalaadimiste arvu"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Sisestage lubatavate paralleelsete allalaadimiste arv.\n"
+" (Sisestage väärtus vahemikus 1 kuni {max_downloads})\n"
+"note:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Keela/Vaikimisi : 0 ( keelab paralleelse allalaadimise, võimaldab ainult 1 allalaadimist korraga )"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]."
+
+#, fuzzy
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway vajab juurdepääsu teie seatile (riistvaraseadmete kogum, st klaviatuur, hiir jne."
+
+#, fuzzy
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Valige valik, et anda Sway'le juurdepääs teie riistvarale"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Kas soovite zramis kasutada swapi?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Kustuta profiil"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Märgistada/mittemärgistada käivitatavak"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Kas soovite kasutada BTRFS-i tihendamist?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/fi/LC_MESSAGES/base.mo b/archinstall/locales/fi/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..e460d89f
--- /dev/null
+++ b/archinstall/locales/fi/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/fi/LC_MESSAGES/base.po b/archinstall/locales/fi/LC_MESSAGES/base.po
new file mode 100644
index 00000000..b5857e69
--- /dev/null
+++ b/archinstall/locales/fi/LC_MESSAGES/base.po
@@ -0,0 +1,1469 @@
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Language-Team: \n"
+"X-Generator: Poedit 2.3\n"
+"Last-Translator: Kimmo Kujansuu <mrkujansuu@gmail.com>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Language: fi\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] Lokitiedosto luotiin tähän: {} {}"
+
+msgid ""
+" Please submit this issue (and file) to https://github.com/archlinux/"
+"archinstall/issues"
+msgstr ""
+" Lähetä tämä ongelma (ja tiedosto) osoitteeseen https://github.com/"
+"archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "Haluatko todella poistua?"
+
+msgid "And one more time for verification: "
+msgstr "Ja vielä kerran vahvistukseksi: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "Haluatko käyttää swap:ia zram:ssa?"
+
+msgid "Desired hostname for the installation: "
+msgstr "Valitse hostname asennukselle: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "Käyttäjänimi superkäyttäjälle, jolla on sudo-oikeudet: "
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "Muut asennettavat käyttäjät (jätä tyhjäksi, jos ei muita käyttäjiä): "
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "Pitäisikö tämän käyttäjän olla superkäyttäjä (sudoer)?"
+
+msgid "Select a timezone"
+msgstr "Valitse aikavyöhyke"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "Haluatko käyttää GRUB:ia lataimena systemd-bootin sijaan?"
+
+msgid "Choose a bootloader"
+msgstr "Valitse käynnistyslatain"
+
+msgid "Choose an audio server"
+msgstr "Valitse audiopalvelin"
+
+msgid ""
+"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr "
+"and optional profile packages are installed."
+msgstr ""
+"Vain paketit, kuten base, base-devel, linux, linux-firmware, efibootmgr ja "
+"valinnaiset profiili-paketit, asennetaan."
+
+msgid ""
+"If you desire a web browser, such as firefox or chromium, you may specify it "
+"in the following prompt."
+msgstr ""
+"Jos haluat selaimen, kuten firefox tai chromium, voit valita sen seuraavassa "
+"kohdassa."
+
+msgid ""
+"Write additional packages to install (space separated, leave blank to skip): "
+msgstr ""
+"Kirjoita asennettavat lisäpaketit (välilyönnillä erotettuna, ohita vaihe "
+"jättämällä tyhjäksi): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr "Kopioi ISO verkkoasetukset asennukseen"
+
+msgid ""
+"Use NetworkManager (necessary for configuring internet graphically in GNOME "
+"and KDE)"
+msgstr ""
+"Käytä NetworkManager ohjelmaa (Internetin graafinen määritys GNOME ja KDE)"
+
+msgid "Select one network interface to configure"
+msgstr "Valitse yksi määritettävä verkkoliitäntä"
+
+msgid ""
+"Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr ""
+"Valitse, mikä tila määritetään \"{}\", tai ohita ja käytä \"{}\" oletusta."
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "Anna IP-osoite ja aliverkko {} (esim. 192.168.0.5/24): "
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "Anna yhdyskäytävän (reitittimen) IP-osoite tai jätä tyhjäksi: "
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr ""
+"Anna DNS-osoitteet (välilyönti erottaa kaksi osoitetta toisistaan, tyhjä ei "
+"mitään): "
+
+msgid "Select which filesystem your main partition should use"
+msgstr "Valitse, mitä tiedostojärjestelmä pääosiolle"
+
+msgid "Current partition layout"
+msgstr "Tämänhetkinen osion asettelu"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"Valitse mitä haluat tehdä\n"
+"{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "Anna osiolle haluttu tiedostojärjestelmän tyyppi"
+
+msgid ""
+"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "Anna aloituspaikka (yksiköt: s, GB, %, etc. ; oletus: {}): "
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "Anna lopetuspaikka (yksiköt: s, GB, %, etc. ; ex: {}): "
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "{} sisältää jonossa olevat osiot, tämä poistaa ne, oletko varma?"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse indeksin perusteella, mitkä osiot haluat poistaa"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse indeksin perusteella, mikä osio liitetään mihin"
+
+msgid ""
+" * Partition mount-points are relative to inside the installation, the boot "
+"would be /boot as an example."
+msgstr ""
+" * Osioiden liitoskohdat ovat suhteessa asennuksen sisäpuolelle, käynnistys "
+"olisi esimerkiksi /boot."
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr ""
+"Valitse, mihin osio asennetaan (jätä tyhjäksi, jos haluat poistaa "
+"liitoskohdan): "
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse, mikä osio merkitään alustusta varten"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse, mikä osio merkitään salattavaksi"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse, mikä osio merkitään käynnistyväksi"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse, mikä osio merkitään tiedostojärjestelmää varten"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "Anna osiolle haluttu tiedostojärjestelmän tyyppi: "
+
+msgid "Archinstall language"
+msgstr "Archinstall kieli"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr ""
+"Pyyhi kaikki valitut asemat ja käytä parasta mahdollista oletusosion "
+"asettelua"
+
+msgid ""
+"Select what to do with each individual drive (followed by partition usage)"
+msgstr ""
+"Valitse, mitä tehdään kullekin yksittäiselle asemalle (ja osion käytölle)"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "Valitse, mitä haluat tehdä valituilla lohkoilla"
+
+msgid ""
+"This is a list of pre-programmed profiles, they might make it easier to "
+"install things like desktop environments"
+msgstr ""
+"Tämä on lista valmiista profiileista, jotka saattavat helpottaa esimerkiksi "
+"työpöytäympäristön asentamista"
+
+msgid "Select keyboard layout"
+msgstr "Valitse näppäimistön asettelu"
+
+msgid "Select one of the regions to download packages from"
+msgstr "Valitse yksi alueista, joista paketit ladataan"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "Valitse yksi tai useampi kiintolevy käytettäväksi ja määritettäväksi"
+
+msgid ""
+"For the best compatibility with your AMD hardware, you may want to use "
+"either the all open-source or AMD / ATI options."
+msgstr ""
+"Saat parhaan yhteensopivuuden AMD:n kanssa käyttämällä joko avoimen "
+"lähdekoodin vaihtoehtoja tai AMD/ATI valmistajan vaihtoehtoja."
+
+msgid ""
+"For the best compatibility with your Intel hardware, you may want to use "
+"either the all open-source or Intel options.\n"
+msgstr ""
+"Saat parhaan yhteensopivuuden Intelin kanssa käyttämällä joko avoimen "
+"lähdekoodin vaihtoehtoja tai Intel valmistajan vaihtoehtoja.\n"
+
+msgid ""
+"For the best compatibility with your Nvidia hardware, you may want to use "
+"the Nvidia proprietary driver.\n"
+msgstr ""
+"Saat parhaan yhteensopivuuden Nvidian kanssa käyttämällä Nvidia valmistajan "
+"omaa ohjainta.\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Valitse näytönohjaimen ohjain tai jätä tyhjäksi niin kaikki asennetaan "
+"avoimen lähdekoodin ohjaimilla"
+
+msgid "All open-source (default)"
+msgstr "Kaikki avoimet lähdekoodit (oletus)"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "Valitse käytettävät kernelit tai jätä tyhjäksi oletuksena \"{}\""
+
+msgid "Choose which locale language to use"
+msgstr "Valitse käytettävä kieli"
+
+msgid "Choose which locale encoding to use"
+msgstr "Valitse käytettävä kielialueen koodaus"
+
+msgid "Select one of the values shown below: "
+msgstr "Valitse jokin alla olevista arvoista: "
+
+msgid "Select one or more of the options below: "
+msgstr "Valitse yksi tai useampi alla olevista: "
+
+msgid "Adding partition...."
+msgstr "Lisätään osiota...."
+
+msgid ""
+"You need to enter a valid fs-type in order to continue. See `man parted` for "
+"valid fs-type's."
+msgstr ""
+"Sinun on annettava kelvollinen fs-tyyppi jatkaaksesi. Katso kelvolliset fs-"
+"tyypit kohdasta \"man parted\"."
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "Virhe: Profiilien luettelointi URL \"{}\" johti:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "Virhe: Tulosta \"{}\" ei voitu purkaa JSON-muodossa:"
+
+msgid "Keyboard layout"
+msgstr "Näppäimistö"
+
+msgid "Mirror region"
+msgstr "Pakettivarasto"
+
+msgid "Locale language"
+msgstr "Kieli"
+
+msgid "Locale encoding"
+msgstr "Kieli-koodaus"
+
+msgid "Drive(s)"
+msgstr "Levyasemat"
+
+msgid "Disk layout"
+msgstr "Levyn asettelu"
+
+msgid "Encryption password"
+msgstr "Salauksen salasana"
+
+msgid "Swap"
+msgstr "Swap"
+
+msgid "Bootloader"
+msgstr "Käynnistyksenlataaja"
+
+msgid "Root password"
+msgstr "Root salasana"
+
+msgid "Superuser account"
+msgstr "Superkäyttäjätili"
+
+msgid "User account"
+msgstr "Käyttäjätili"
+
+msgid "Profile"
+msgstr "Profiili"
+
+msgid "Audio"
+msgstr "Audio"
+
+msgid "Kernels"
+msgstr "Kernelit"
+
+msgid "Additional packages"
+msgstr "Lisäpaketit"
+
+msgid "Network configuration"
+msgstr "Verkkoasetukset"
+
+msgid "Automatic time sync (NTP)"
+msgstr "Autom. kellonajan synkronointi (NTP)"
+
+msgid "Install ({} config(s) missing)"
+msgstr "Asenna ({} asetusta puuttuu)"
+
+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 ""
+"Päätit ohittaa kiintolevyn valinnan ja käyttää\n"
+"mitä tahansa asema-asetusta {} (kokeellinen)\n"
+"VAROITUS: Archinstall ei voi tarkista asennuksen sopivuutta\n"
+"Haluatko jatkaa?"
+
+msgid "Re-using partition instance: {}"
+msgstr "Käytetään osiointi esimerkkiä: {}"
+
+msgid "Create a new partition"
+msgstr "Luo uusi osio"
+
+msgid "Delete a partition"
+msgstr "Poista osio"
+
+msgid "Clear/Delete all partitions"
+msgstr "Tyhjennä/poista kaikki osiot"
+
+msgid "Assign mount-point for a partition"
+msgstr "Määritä osion liitoskohta"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "Merkitse/poista osion alustaminen (pyyhkii tiedot)"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "Merkitse/poista osio salataan merkintä"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "Merkitse/poista osion merkintä käynnistyväksi (automaattinen /boot)"
+
+msgid "Set desired filesystem for a partition"
+msgstr "Aseta osiolle haluttu tiedostojärjestelmä"
+
+msgid "Abort"
+msgstr "Keskeytä"
+
+msgid "Hostname"
+msgstr "Hostname \"kone-nimi\""
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "Ei määritetty, ei käytettävissä, ellei asennusta ole käsin määritetty"
+
+msgid "Timezone"
+msgstr "Aikavyöhyke"
+
+msgid "Set/Modify the below options"
+msgstr "Aseta/muokkaa alla olevia asetuksia"
+
+msgid "Install"
+msgstr "Asenna"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"Ohita painamalla ESC\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "Ehdota osion asettelua"
+
+msgid "Enter a password: "
+msgstr "Anna salasana: "
+
+msgid "Enter a encryption password for {}"
+msgstr "Anna salauksen salasana {}"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "Anna levyn salauksen salasana (jätä tyhjäksi, jos et salaa): "
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "Luo vaadittu superkäyttäjä, jolla on sudo-oikeudet: "
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "Anna root-salasana (jätä tyhjäksi niin root poistetaan käytöstä): "
+
+msgid "Password for user \"{}\": "
+msgstr "Salasana käyttäjälle \"{}\": "
+
+msgid ""
+"Verifying that additional packages exist (this might take a few seconds)"
+msgstr ""
+"Varmistetaan, että lisäpaketit ovat olemassa (saattaa kestää muutaman "
+"sekunnin)"
+
+msgid ""
+"Would you like to use automatic time synchronization (NTP) with the default "
+"time servers?\n"
+msgstr ""
+"Haluatko käyttää automaattista ajan synkronointia oletus (NTP) "
+"aikapalvelinten kanssa?\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 ""
+"NTP:n toiminta saattaa vaatia laitteistolle muita määrityksen jälkeisiä "
+"vaiheita.\n"
+"Katso lisätietoja Arch-wiki"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "Luo uusi käyttäjä antamalla käyttäjänimi (jätä tyhjäksi ja ohita): "
+
+msgid "Use ESC to skip\n"
+msgstr "Ohita painamalla ESC\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for "
+"it to execute"
+msgstr ""
+"\n"
+"Valitse suoritettava toiminto luettelosta"
+
+msgid "Cancel"
+msgstr "Peruuta"
+
+msgid "Confirm and exit"
+msgstr "Vahvista ja poistu"
+
+msgid "Add"
+msgstr "Lisää"
+
+msgid "Copy"
+msgstr "Kopioi"
+
+msgid "Edit"
+msgstr "Muokkaa"
+
+msgid "Delete"
+msgstr "Poista"
+
+msgid "Select an action for '{}'"
+msgstr "Valitse toiminta \"{}\""
+
+msgid "Copy to new key:"
+msgstr "Kopioi uuteen avaimeen:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "Tuntematon verkkokortin tyyppi: {}. Mahdolliset arvot ovat {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"Tämä on sinun valitsema kokoonpano:"
+
+msgid ""
+"Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "Pacman on jo käynnissä, odota enintään 10 minuuttia sen päättymistä."
+
+msgid ""
+"Pre-existing pacman lock never exited. Please clean up any existing pacman "
+"sessions before using archinstall."
+msgstr ""
+"Aiempi pacman-lukko ei ole poistunut. Lopeta kaikki olemassa olevat pacman-"
+"istunnot ennen archinstall ohjelman käyttöä."
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "Valitse, valinnaiset lisävarastot jotka otetaan käyttöön"
+
+msgid "Add a user"
+msgstr "Lisää käyttäjä"
+
+msgid "Change password"
+msgstr "Vaihda salasana"
+
+msgid "Promote/Demote user"
+msgstr "Korota/alenna käyttäjä"
+
+msgid "Delete User"
+msgstr "Poista käyttäjä"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"Määritä uusi käyttäjä\n"
+
+msgid "User Name : "
+msgstr "Käyttäjänimi : "
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "Pitäisikö {} olla superkäyttäjä (sudoer)?"
+
+msgid "Define users with sudo privilege: "
+msgstr "Määritä käyttäjät, joilla on sudo-oikeudet: "
+
+msgid "No network configuration"
+msgstr "Ei verkkoasetuksia"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "Aseta haluamasi alitaltiot btrfs-osioon"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}\n"
+"\n"
+"Valitse, mille osioille alitaltiot asetetaan"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "Hallitse nykyisen osion btrfs-alitaltioita"
+
+msgid "No configuration"
+msgstr "Ei määritystä"
+
+msgid "Save user configuration"
+msgstr "Tallenna käyttäjän määritykset"
+
+msgid "Save user credentials"
+msgstr "Tallenna käyttäjän tunnistetiedot"
+
+msgid "Save disk layout"
+msgstr "Tallenna levyasettelu"
+
+msgid "Save all"
+msgstr "Tallenna kaikki"
+
+msgid "Choose which configuration to save"
+msgstr "Valitse tallennettavat määritykset"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "Anna hakemisto tallennettaville määrityksille: "
+
+msgid "Not a valid directory: {}"
+msgstr "Hakemisto ei ole kelvollinen: {}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "Käyttämäsi salasana vaikuttaa heikolta,"
+
+msgid "are you sure you want to use it?"
+msgstr "oletko varma että haluat käyttää sitä?"
+
+msgid "Optional repositories"
+msgstr "Valinnaiset arkistot"
+
+msgid "Save configuration"
+msgstr "Tallenna määritykset"
+
+msgid "Missing configurations:\n"
+msgstr "Puuttuvat määritykset:\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "Joko root-salasana tai vähintään yksi pääkäyttäjä on määritettävä"
+
+msgid "Manage superuser accounts: "
+msgstr "Hallitse superkäyttäjätilejä: "
+
+msgid "Manage ordinary user accounts: "
+msgstr "Hallitse tavallisia käyttäjätilejä: "
+
+msgid " Subvolume :{:16}"
+msgstr " Alitaltio :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " liitetty {:16}"
+
+msgid " with option {}"
+msgstr " valinnalla {}"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+" Täytä halutut arvot uudelle alitaltiolle \n"
+
+msgid "Subvolume name "
+msgstr "Alitaltion nimi "
+
+msgid "Subvolume mountpoint"
+msgstr "Alitaltion liitoskohta"
+
+msgid "Subvolume options"
+msgstr "Alitaltion valinnat"
+
+msgid "Save"
+msgstr "Tallenna"
+
+msgid "Subvolume name :"
+msgstr "Alitaltion nimi :"
+
+msgid "Select a mount point :"
+msgstr "Valitse liitoskohta :"
+
+msgid "Select the desired subvolume options "
+msgstr "Valitse alitaltion asetukset "
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "Määritä käyttäjät, joilla on sudo-oikeudet, käyttäjänimen mukaan: "
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] Lokitiedosto on luotu tähän: {}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "Haluatko käyttää BTRFS-alitaltioita oletusrakenteena?"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "Haluatko käyttää BTRFS-pakkausta?"
+
+msgid "Would you like to create a separate partition for /home?"
+msgstr "Haluatko luoda erillisen osion /home ?"
+
+msgid ""
+"The selected drives do not have the minimum capacity required for an "
+"automatic suggestion\n"
+msgstr ""
+"Valituilla asemilla ei ole automaattiseen ehdotukseen vaadittavaa "
+"vähimmäistilaa\n"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "Minimi tila /home osiolle: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "Minimi tila Arch Linux osiolle: {}GB"
+
+msgid "Continue"
+msgstr "Jatka"
+
+msgid "yes"
+msgstr "kyllä"
+
+msgid "no"
+msgstr "ei"
+
+msgid "set: {}"
+msgstr "aseta: {}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "Manuaaliset asetukset on oltava lista muodossa"
+
+msgid "No iface specified for manual configuration"
+msgstr "Manuaalista määritystä varten iface on tekemättä"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr ""
+"Manuaalinen verkkokortin määritys ilman autom. DHCP:tä vaatii IP-osoitteen"
+
+msgid "Add interface"
+msgstr "Lisää liitäntä"
+
+msgid "Edit interface"
+msgstr "Muokkaa liitäntää"
+
+msgid "Delete interface"
+msgstr "Poista liitäntä"
+
+msgid "Select interface to add"
+msgstr "Valitse lisättävä liitäntä"
+
+msgid "Manual configuration"
+msgstr "Käsin määrittely"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "Merkitse/poista osio pakatuksi (vain btrfs)"
+
+msgid ""
+"The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "Käyttämäsi salasana vaikuttaa heikolta, haluatko käyttää sitä?"
+
+msgid ""
+"Provides a selection of desktop environments and tiling window managers, e."
+"g. gnome, kde, sway"
+msgstr ""
+"Tarjoaa valikoiman erilaisia työpöytiä ja ikkunointia esim. gnome, kde, sway"
+
+msgid "Select your desired desktop environment"
+msgstr "Valitse haluamasi työpöytä"
+
+msgid ""
+"A very basic installation that allows you to customize Arch Linux as you see "
+"fit."
+msgstr ""
+"Hyvin kevyt asennus, jonka avulla voit mukauttaa Arch Linuxia haluamallasi "
+"tavalla."
+
+msgid ""
+"Provides a selection of various server packages to install and enable, e.g. "
+"httpd, nginx, mariadb"
+msgstr ""
+"Tarjoaa valikoiman erilaisia palvelinpaketteja ja saatavilla käyttöön esim. "
+"httpd, nginx, mariadb"
+
+msgid ""
+"Choose which servers to install, if none then a minimal installation will be "
+"done"
+msgstr ""
+"Valitse mitkä palvelimet asennetaan. Jos ei yhtään, tehdään pienin "
+"mahdollinen asennus"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr ""
+"Asentaa minimaalisen järjestelmän sekä xorg ja ohjaimet näytönohjaimelle."
+
+msgid "Press Enter to continue."
+msgstr "Paina ENTER ja jatka."
+
+msgid ""
+"Would you like to chroot into the newly created installation and perform "
+"post-installation configuration?"
+msgstr ""
+"Haluatko chroot:in uuteen asennukseen ja suorittaa asennuksen jälkeiset "
+"asetukset?"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "Haluatko varmasti nollata tämän asetuksen?"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "Valitse yksi tai useampi kiintolevy määritettäväksi\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "Kaikki muutokset olemassa olevaan asetukseen nollaavat levyasettelun!"
+
+msgid ""
+"If you reset the harddrive selection this will also reset the current disk "
+"layout. Are you sure?"
+msgstr ""
+"Jos nollaat kiintolevyn valinnan, tämä nollaa myös nykyisen levyasettelun. "
+"Oletko varma?"
+
+msgid "Save and exit"
+msgstr "Tallenna ja poistu"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"{}\n"
+"sisältää jonossa olevia osioita, tämä poistaa ne, oletko varma?"
+
+msgid "No audio server"
+msgstr "Ei audiopalvelinta"
+
+msgid "(default)"
+msgstr "(oletus)"
+
+msgid "Use ESC to skip"
+msgstr "Ohita painamalla ESC"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+"CTRL+C nollaa nykyisen valinnan\n"
+"\n"
+
+msgid "Copy to: "
+msgstr "Kopioi…: "
+
+msgid "Edit: "
+msgstr "Muokkaa: "
+
+msgid "Key: "
+msgstr "Avain: "
+
+msgid "Edit {}: "
+msgstr "Muokkaa {}: "
+
+msgid "Add: "
+msgstr "Lisää: "
+
+msgid "Value: "
+msgstr "Arvo: "
+
+msgid ""
+"You can skip selecting a drive and partitioning and use whatever drive-setup "
+"is mounted at /mnt (experimental)"
+msgstr ""
+"Voit ohittaa aseman valinnan, sekä osioinnin ja käyttää mitä tahansa asemaa, "
+"joka on kytketty /mnt hakemistossa (kokeellinen)"
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "Valitse jokin levyistä tai ohita ja käytä /mnt oletuksena"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "Valitse alustettavat osiot:"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "Käytä HSM:ää avataksesi salatun aseman"
+
+msgid "Device"
+msgstr "Laite"
+
+msgid "Size"
+msgstr "Koko"
+
+msgid "Free space"
+msgstr "Vapaata tilaa"
+
+msgid "Bus-type"
+msgstr "Väylän tyyppi"
+
+msgid ""
+"Either root-password or at least 1 user with sudo privileges must be "
+"specified"
+msgstr ""
+"Joko root-salasana tai vähintään yksi käyttäjä, jolla on sudo-oikeudet, on "
+"määritettävä"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "Anna käyttäjänimi (jätä tyhjäksi ja ohita): "
+
+msgid "The username you entered is invalid. Try again"
+msgstr "Antamasi käyttäjänimi ei kelpaa. Yritä uudelleen"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "Pitäisikö \"{}\" olla superkäyttäjä (sudo)?"
+
+msgid "Select which partitions to encrypt"
+msgstr "Valitse salattavat osiot"
+
+msgid "very weak"
+msgstr "hyvin heikko"
+
+msgid "weak"
+msgstr "heikko"
+
+msgid "moderate"
+msgstr "kohtalainen"
+
+msgid "strong"
+msgstr "vahva"
+
+msgid "Add subvolume"
+msgstr "Lisää alitaltio"
+
+msgid "Edit subvolume"
+msgstr "Muokkaa alitaltiota"
+
+msgid "Delete subvolume"
+msgstr "Poista alitaltio"
+
+msgid "Configured {} interfaces"
+msgstr "Määritetty {} liittymää"
+
+msgid ""
+"This option enables the number of parallel downloads that can occur during "
+"installation"
+msgstr ""
+"Tämä valinta mahdollistaa asennuksen aikana tapahtuvien rinnakkaisten "
+"latausten määrän"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"Anna rinnakkaisten latausten määrä.\n"
+" (anna arvo väliltä 1 - {})\n"
+"Huomaa:"
+
+msgid ""
+" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads "
+"at a time )"
+msgstr ""
+" - Suurin arvo : {} ( sallii {} rinnakkaista latausta, sallii {} latausta "
+"kerralla )"
+
+msgid ""
+" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a "
+"time )"
+msgstr ""
+" - Pienin arvo : 1 ( sallii 1 rinnakkaisen latauksen, sallii {} 2 latausta "
+"kerralla )"
+
+msgid ""
+" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 "
+"download at a time )"
+msgstr ""
+" - Pois/oletus : 0 ( ei salli rinnakkaisia latauksia, sallii vain 1 "
+"latauksen kerralla )"
+
+#, python-brace-format
+msgid ""
+"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to "
+"disable]"
+msgstr ""
+"Virheellinen arvo! Yritä uudelleen kelvollisella arvolla [1 - "
+"{max_downloads}, tai 0 on poistettu]"
+
+msgid "Parallel Downloads"
+msgstr "Rinnakkaiset lataukset"
+
+msgid "ESC to skip"
+msgstr "Ohita painamalla ESC"
+
+msgid "CTRL+C to reset"
+msgstr "CTRL+C nollaa"
+
+msgid "TAB to select"
+msgstr "TAB valitsee"
+
+msgid "[Default value: 0] > "
+msgstr "[oletusarvo: 0] > "
+
+msgid ""
+"To be able to use this translation, please install a font manually that "
+"supports the language."
+msgstr ""
+"Jotta voit käyttää tätä käännöstä, asenna käsin fontti, joka tukee tätä "
+"kieltä."
+
+msgid "The font should be stored as {}"
+msgstr "Fontti tulee tallentaa {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+"Archinstall vaatii toimiakseen root oikeudet. Katso --help ja saat "
+"lisätietoja."
+
+msgid "Select an execution mode"
+msgstr "Valitse suoritustila"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Profiilia ei voi hakea määritetystä URL-osoitteesta: {}"
+
+msgid ""
+"Profiles must have unique name, but profile definitions with duplicate name "
+"found: {}"
+msgstr ""
+"Profiilin nimi on oltava yksilöllinen, mutta määrityksistä löytyy "
+"päällekkäinen nimi: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Valitse yksi tai useampi laite määritettäväksi"
+
+msgid ""
+"If you reset the device selection this will also reset the current disk "
+"layout. Are you sure?"
+msgstr ""
+"Jos nollaat laitevalinnan, tämä nollaa myös nykyisen levyasettelun. Oletko "
+"varma?"
+
+msgid "Existing Partitions"
+msgstr "Olemassa olevat osiot"
+
+msgid "Select a partitioning option"
+msgstr "Valitse osiointivaihtoehto"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Anna root hahemisto liitetylle laitteelle: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Minimi tila /home osiolle: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Minimi tila Arch Linux osiolle: {}GiB"
+
+msgid ""
+"This is a list of pre-programmed profiles_bck, they might make it easier to "
+"install things like desktop environments"
+msgstr ""
+"Lista esiohjelmoiduista profiles_bck paketeista, joka voivat helpottaa "
+"asioita, kuten työpöydän asentamista"
+
+msgid "Current profile selection"
+msgstr "Nykyinen profiilivalinta"
+
+msgid "Remove all newly added partitions"
+msgstr "Poista kaikki juuri lisätyt osiot"
+
+msgid "Assign mountpoint"
+msgstr "Määritä liitoskohta"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Merkitse/poista osion alustaminen (pyyhkii tiedot)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Merkitse/poista käynnistyvä"
+
+msgid "Change filesystem"
+msgstr "Vaihda tiedostojärjestelmä"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Merkitse/poista pakatuksi"
+
+msgid "Set subvolumes"
+msgstr "Aseta alitaltiot"
+
+msgid "Delete partition"
+msgstr "Poista osio"
+
+msgid "Partition"
+msgstr "Osio"
+
+msgid ""
+"This partition is currently encrypted, to format it a filesystem has to be "
+"specified"
+msgstr ""
+"Tämä osio on tällä hetkellä salattu, joten sen alustamiseksi on määritettävä "
+"tiedostojärjestelmä"
+
+msgid ""
+"Partition mount-points are relative to inside the installation, the boot "
+"would be /boot as an example."
+msgstr ""
+"Osioiden liitoskohdat ovat suhteessa asennuksen sisäpuolelle, käynnistys "
+"olisi esimerkiksi /boot."
+
+msgid ""
+"If mountpoint /boot is set, then the partition will also be marked as "
+"bootable."
+msgstr ""
+"Jos liitoskohta /boot on asetettu, myös osio merkitään käynnistettäväksi."
+
+msgid "Mountpoint: "
+msgstr "Liitoskohta: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Laitteen nykyiset vapaat sektorit {}:"
+
+msgid "Total sectors: {}"
+msgstr "Sektoreita kaikkiaan: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Anna aloitussektori (oletus: {}): "
+
+msgid ""
+"Enter the end sector of the partition (percentage or block number, default: "
+"{}): "
+msgstr "Anna osion loppusektori (prosenttina tai lohkonumerona, oletus:: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Tämä poistaa kaikki juuri lisätyt osiot, jatkaako?"
+
+msgid "Partition management: {}"
+msgstr "Osioiden hallinta: {}"
+
+msgid "Total length: {}"
+msgstr "Kokonaispituus: {}"
+
+msgid "Encryption type"
+msgstr "Salaustyyppi"
+
+msgid "Partitions"
+msgstr "Osiot"
+
+msgid "No HSM devices available"
+msgstr "Ei HSM-laitteita saatavilla"
+
+msgid "Partitions to be encrypted"
+msgstr "Salattavat osiot"
+
+msgid "Select disk encryption option"
+msgstr "Valitse levyn salausasetus"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "Valitse HSM:ään käytettävä FIDO2-laite"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Käytä parasta mahdollista oletusosion asettelua"
+
+msgid "Manual Partitioning"
+msgstr "Osiointi käsin"
+
+msgid "Pre-mounted configuration"
+msgstr "Valmiiksi liitetty kokoonpano"
+
+msgid "Unknown"
+msgstr "Tuntematon"
+
+msgid "Partition encryption"
+msgstr "Osion salaus"
+
+msgid " ! Formatting {} in "
+msgstr " ! Alustetaan {} "
+
+msgid "↠Back"
+msgstr "↠Takaisin"
+
+msgid "Disk encryption"
+msgstr "Levyn salaus"
+
+msgid "Configuration"
+msgstr "Asetukset"
+
+msgid "Password"
+msgstr "Salasana"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "Kaikki asetukset nollataan, oletko varma?"
+
+msgid "Back"
+msgstr "Takaisin"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Valitse asennettava käyttöliittymä valituille profiileille: {}"
+
+msgid "Environment type: {}"
+msgstr "Ympäristötyyppi: {}"
+
+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 ""
+"Sway ei tue patentoitua Nvidia-ohjainta. Todennäköistä, että kohtaat joitain "
+"ongelmia, kelpaako se silti sinulle?"
+
+msgid "Installed packages"
+msgstr "Asennetut paketit"
+
+msgid "Add profile"
+msgstr "Lisää profiili"
+
+msgid "Edit profile"
+msgstr "Muokkaa profiilia"
+
+msgid "Delete profile"
+msgstr "Poista profiili"
+
+msgid "Profile name: "
+msgstr "Profiilinimi: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Antamasi profiilinimi on jo käytössä. Yritä uudelleen"
+
+msgid ""
+"Packages to be install with this profile (space separated, leave blank to "
+"skip): "
+msgstr ""
+"Tällä profiililla asennettavat paketit (välilyönnillä erotettuna, ohita "
+"jättämällä tyhjäksi): "
+
+msgid ""
+"Services to be enabled with this profile (space separated, leave blank to "
+"skip): "
+msgstr ""
+"Palvelut, jotka otetaan käyttöön tällä profiililla (välilyönnillä "
+"erotettuna, ohita jättämällä tyhjäksi): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Pitäisikö tämä profiili ottaa käyttöön asennusta varten?"
+
+msgid "Create your own"
+msgstr "Luo sinun oma"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"Valitse näytönohjaimen ohjain tai jätä tyhjäksi niin kaikki asennetaan "
+"avoimen lähdekoodin ohjaimilla"
+
+msgid ""
+"Sway needs access to your seat (collection of hardware devices i.e. "
+"keyboard, mouse, etc)"
+msgstr ""
+"Sway tarvitsee pääsyn istuntoon (kokoelma laitteistoja, kuten näppäimistö, "
+"hiiri jne.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Valitse asetus antaaksesi Swaylle pääsyn laitteistoosi"
+
+msgid "Graphics driver"
+msgstr "Näytönohjaimen ohjain"
+
+msgid "Greeter"
+msgstr "Käyttöliittymä"
+
+msgid "Please chose which greeter to install"
+msgstr "Valitse asennettava käyttöliittymä"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Tämä on lista esiohjelmoiduista default_profiles"
+
+msgid "Disk configuration"
+msgstr "Kiintolevyn määritys"
+
+msgid "Profiles"
+msgstr "Profiilit"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Etsitään hakemistoja asetustiedostojen tallentamiseen ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Valitse hakemisto (tai hakemistot) asetustiedostojen tallentamiseen"
+
+msgid "Add a custom mirror"
+msgstr "Lisää mukautettu peilipaikka"
+
+msgid "Change custom mirror"
+msgstr "Vaihda mukautettu peilipaikka"
+
+msgid "Delete custom mirror"
+msgstr "Poista mukautettu peilipaikka"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Anna nimi (jätä tyhjäksi ja ohita): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Anna url (jätä tyhjäksi ja ohita): "
+
+msgid "Select signature check option"
+msgstr "Valitse allekirjoituksen tarkistusvaihtoehto"
+
+msgid "Select signature option"
+msgstr "Valitse allekirjoituksen asetus"
+
+msgid "Custom mirrors"
+msgstr "Mukautetut peilipaikat"
+
+msgid "Defined"
+msgstr "Määritelty"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Tallenna käyttäjän asetukset (myös levyasettelu)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion "
+"enabled)\n"
+"Save directory: "
+msgstr ""
+"Anna hakemisto tallennettaville määrityksille (välilehden viimeistely "
+"käytössä)\n"
+"Tallennushakemisto: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Haluatko tallentaa {} asetustiedostoa seuraavaan paikkaan?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Tallennetaan {} määritystiedostoa {}"
+
+msgid "Mirrors"
+msgstr "Peilipaikat"
+
+msgid "Mirror regions"
+msgstr "Peilipaikan maa"
+
+msgid ""
+" - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads"
+"+1} downloads at a time )"
+msgstr ""
+" - Suurin arvo : {} ( sallii {} rinnakkaista latausta, sallii "
+"{max_downloads+1} latausta kerralla )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+"Virheellinen arvo! Yritä uudelleen kelvollisella arvolla [1 - {}, tai 0 on "
+"poistettu]"
+
+msgid "Locales"
+msgstr "Alueet"
+
+msgid ""
+"Use NetworkManager (necessary to configure internet graphically in GNOME and "
+"KDE)"
+msgstr ""
+"Käytä NetworkManager -ohjelmaa (Internetin määritys graafisesti GNOME ja KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Yhteensä: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+"Kaikki annetut arvot voidaan liittää käyttäen yksikköjä: B, KB, KiB, MB, "
+"MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Jos yksikköä ei ole annettu, arvo tulkitaan sektoreiksi"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Anna alku (oletus: sektori {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Anna loppu (oletus: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Fido2-laitteita ei voida määrittää. Onko libfido2 asennettu?"
+
+msgid "Path"
+msgstr "Polku"
+
+msgid "Manufacturer"
+msgstr "Valmistaja"
+
+msgid "Product"
+msgstr "Tuote"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Virheellinen määritys: {error}"
+
+msgid "Type"
+msgstr "Tyyppi"
+
+msgid ""
+"This option enables the number of parallel downloads that can occur during "
+"package downloads"
+msgstr ""
+"Tämä valinta mahdollistaa asennuksen aikana tapahtuvien rinnakkaisten "
+"latausten määrän"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Anna rinnakkaisten latausten määrä.\n"
+"\n"
+"Huomaa:\n"
+
+msgid ""
+" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+" - Suurin suositeltu arvo : {} ( sallii {} rinnakkaista latausta kerralla )"
+
+msgid ""
+" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 "
+"download at a time )\n"
+msgstr ""
+" - Pois/oletus : 0 ( ei salli rinnakkaisia latauksia, sallii vain 1 "
+"latauksen kerralla )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+"Virheellinen arvo! Yritä uudelleen kelvollisella arvolla [tai 0 on poistettu]"
+
+msgid ""
+"Hyprland needs access to your seat (collection of hardware devices i.e. "
+"keyboard, mouse, etc)"
+msgstr ""
+"Hyprland tarvitsee pääsyn istuntoon (kokoelma laitteistoja, kuten "
+"näppäimistö, hiiri jne.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Valitse asetus antaaksesi Hyprlandille pääsyn laitteistoosi"
+
+msgid ""
+"All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+"Kaikki annetut arvot voidaan liittää käyttäen yksikköjä: B, KB, KiB, MB, "
+"MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr ""
+"Haluatko käyttää Unified Kernel Images, joka voidaan käynnistää suoraan UEFI-"
+"ohjelmistosta?"
+
+msgid "Unified kernel images"
+msgstr "UKI Unified Kernel Images"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Odotetaan ajan synkronoinnin (timedatectl show) päättymistä."
+
+msgid ""
+"Time syncronization not completing, while you wait - check the docs for "
+"workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+"Ajan synkronointi ei ole valmis, kun odotat voit tarkistaa kiertotapoja: "
+"https://archinstall.readthedocs.io/"
+
+msgid ""
+"Skipping waiting for automatic time sync (this can cause issues if time is "
+"out of sync during installation)"
+msgstr ""
+"Autom. ajan synkronoinnin odottaminen ohitetaan (voi aiheuttaa ongelmia, jos "
+"aikaa ei ole synkronoitu asennuksen aikana)"
+
+msgid ""
+"Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+"Odotetaan Arch Linux avainten synkronoinnin valmistumista (archlinux-keyring-"
+"wkd-sync)."
+
+msgid "Selected profiles: "
+msgstr "Valitut profiilit: "
+
+msgid ""
+"Time synchronization not completing, while you wait - check the docs for "
+"workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+"Ajan synkronointi ei ole valmis, kun odotat voit tarkistaa kiertotapoja: "
+"https://archinstall.readthedocs.io/"
+
+msgid "Mark/Unmark as nodatacow"
+msgstr "Merkitse/poista nodatacow merkintä"
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Haluatko käyttää pakkausta vai poistaa CoW:n käytöstä?"
+
+msgid "Use compression"
+msgstr "Käytä pakkausta"
+
+msgid "Disable Copy-on-Write"
+msgstr "Poista kopiointi kirjoittamisen yhteydessä"
diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo
index 16b8a6cf..fdf30711 100644
--- a/archinstall/locales/fr/LC_MESSAGES/base.mo
+++ b/archinstall/locales/fr/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po
index 76080e40..409af320 100644
--- a/archinstall/locales/fr/LC_MESSAGES/base.po
+++ b/archinstall/locales/fr/LC_MESSAGES/base.po
@@ -2,20 +2,24 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Last-Translator: \n"
+"PO-Revision-Date: 2024-04-23 21:38+0200\n"
+"Last-Translator: Roxfr <roxfr@outlook.fr>\n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.2.2\n"
+"X-Generator: Poedit 3.4.2\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] Un fichier journal a été créé ici : {} {}"
-msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
-msgstr " Veuillez soumettre ce problème (et le fichier) à https://github.com/archlinux/archinstall/issues"
+msgid ""
+" Please submit this issue (and file) to https://github.com/archlinux/"
+"archinstall/issues"
+msgstr ""
+" Veuillez soumettre ce problème (et le fichier) à https://github.com/"
+"archlinux/archinstall/issues"
msgid "Do you really want to abort?"
msgstr "Voulez-vous vraiment abandonner ?"
@@ -30,10 +34,13 @@ msgid "Desired hostname for the installation: "
msgstr "Nom d'hôte souhaité pour l'installation : "
msgid "Username for required superuser with sudo privileges: "
-msgstr "Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : "
+msgstr ""
+"Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : "
msgid "Any additional users to install (leave blank for no users): "
-msgstr "Utilisateur supplémentaire à installer (laisser vide pour aucun utilisateur) : "
+msgstr ""
+"Utilisateur supplémentaire à installer (laisser vide pour aucun "
+"utilisateur) : "
msgid "Should this user be a superuser (sudoer)?"
msgstr "Cet utilisateur doit-il être un superutilisateur (sudoer) ?"
@@ -42,7 +49,9 @@ msgid "Select a timezone"
msgstr "Sélectionner un fuseau horaire"
msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
-msgstr "Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-boot ?"
+msgstr ""
+"Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-"
+"boot ?"
msgid "Choose a bootloader"
msgstr "Choisir un chargeur de démarrage"
@@ -50,38 +59,60 @@ msgstr "Choisir un chargeur de démarrage"
msgid "Choose an audio server"
msgstr "Choisir un serveur audio"
-msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
-msgstr "Seuls les packages tels que base, base-devel, linux, linux-firmware, efibootmgr et les packages de profil optionnels sont installés."
+msgid ""
+"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr "
+"and optional profile packages are installed."
+msgstr ""
+"Seuls les paquets tels que base, base-devel, linux, linux-firmware, "
+"efibootmgr et les paquets de profil optionnels sont installés."
-msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le spécifier dans l'invite suivante."
+msgid ""
+"If you desire a web browser, such as firefox or chromium, you may specify it "
+"in the following prompt."
+msgstr ""
+"Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le "
+"spécifier dans l'invite suivante."
-msgid "Write additional packages to install (space separated, leave blank to skip): "
-msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : "
+msgid ""
+"Write additional packages to install (space separated, leave blank to skip): "
+msgstr ""
+"Saisir les paquets supplémentaires à installer (séparés par des espaces, "
+"vide pour ignorer) : "
msgid "Copy ISO network configuration to installation"
msgstr "Copier la configuration réseau ISO dans l'installation"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
-msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)"
+msgid ""
+"Use NetworkManager (necessary for configuring internet graphically in GNOME "
+"and KDE)"
+msgstr ""
+"Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet "
+"dans GNOME et KDE)"
msgid "Select one network interface to configure"
msgstr "Sélectionner une interface réseau à configurer"
-msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgid ""
+"Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
msgstr ""
+"Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le "
+"mode par défaut \"{}\""
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
msgstr "Entrer l'IP et le sous-réseau pour {} (exemple : 192.168.0.5/24) : "
msgid "Enter your gateway (router) IP address or leave blank for none: "
-msgstr "Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour aucune : "
+msgstr ""
+"Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour "
+"aucune : "
msgid "Enter your DNS servers (space separated, blank for none): "
msgstr "Entrer vos serveurs DNS (séparés par des espaces, vide pour aucun) : "
msgid "Select which filesystem your main partition should use"
-msgstr "Sélectionner le système de fichiers que votre partition principale doit utiliser"
+msgstr ""
+"Sélectionner le système de fichiers que votre partition principale doit "
+"utiliser"
msgid "Current partition layout"
msgstr "Disposition actuelle des partitions"
@@ -96,14 +127,21 @@ msgstr ""
msgid "Enter a desired filesystem type for the partition"
msgstr "Entrer un type de système de fichiers souhaité pour la partition"
-msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgid ""
+"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
msgstr ""
+"Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par "
+"défaut : {}) : "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
msgstr ""
+"Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : "
+"{}) : "
msgid "{} contains queued partitions, this will remove those, are you sure?"
-msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?"
+msgstr ""
+"{} contient des partitions en file d'attente, cela les supprimera, êtes-vous "
+"sûr ?"
msgid ""
"{}\n"
@@ -123,11 +161,17 @@ msgstr ""
"\n"
"Sélectionner par index où et quelle partition montée"
-msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
-msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple."
+msgid ""
+" * Partition mount-points are relative to inside the installation, the boot "
+"would be /boot as an example."
+msgstr ""
+" * Les points de montage de la partition sont relatifs à l'intérieur de "
+"l'installation, le démarrage serait /boot par exemple."
msgid "Select where to mount partition (leave blank to remove mountpoint): "
-msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : "
+msgstr ""
+"Sélectionner où monter la partition (laisser vide pour supprimer le point de "
+"montage) : "
msgid ""
"{}\n"
@@ -166,40 +210,64 @@ msgstr ""
"Sélectionner la partition sur laquelle définir un système de fichiers"
msgid "Enter a desired filesystem type for the partition: "
-msgstr "Entrer un type de système de fichiers souhaité pour la partition : "
+msgstr "Entrer un type de système de fichiers souhaité pour la partition : "
msgid "Archinstall language"
msgstr "Langue d'Archinstall"
msgid "Wipe all selected drives and use a best-effort default partition layout"
-msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale"
+msgstr ""
+"Effacer tous les lecteurs sélectionnés et utiliser une disposition de "
+"partition par défaut optimale"
-msgid "Select what to do with each individual drive (followed by partition usage)"
-msgstr "Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de l'utilisation de la partition)"
+msgid ""
+"Select what to do with each individual drive (followed by partition usage)"
+msgstr ""
+"Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de "
+"l'utilisation de la partition)"
msgid "Select what you wish to do with the selected block devices"
-msgstr "Sélectionner ce que vous souhaitez faire avec les périphériques de bloc sélectionnés"
+msgstr ""
+"Sélectionner ce que vous souhaitez faire avec les périphériques de bloc "
+"sélectionnés"
-msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
-msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau"
+msgid ""
+"This is a list of pre-programmed profiles, they might make it easier to "
+"install things like desktop environments"
+msgstr ""
+"Ceci est une liste préprogrammée de profiles, ils pourraient faciliter "
+"l'installation d'outils comme les environnements de bureau"
msgid "Select keyboard layout"
msgstr "Sélectionner la disposition du clavier"
msgid "Select one of the regions to download packages from"
-msgstr "Sélectionner l'une des régions depuis lesquelles télécharger les packages"
+msgstr ""
+"Sélectionner l'une des régions depuis lesquelles télécharger les paquets"
msgid "Select one or more hard drives to use and configure"
msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer"
-msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez utiliser les options entièrement open source ou AMD / ATI."
+msgid ""
+"For the best compatibility with your AMD hardware, you may want to use "
+"either the all open-source or AMD / ATI options."
+msgstr ""
+"Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez "
+"utiliser les options entièrement open source ou AMD / ATI."
-msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
-msgstr "Pour une compatibilité optimale avec votre matériel Intel, vous pouvez utiliser les options entièrement open source ou Intel.\n"
+msgid ""
+"For the best compatibility with your Intel hardware, you may want to use "
+"either the all open-source or Intel options.\n"
+msgstr ""
+"Pour une compatibilité optimale avec votre matériel Intel, vous pouvez "
+"utiliser les options entièrement open source ou Intel.\n"
-msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
-msgstr "Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez utiliser le pilote propriétaire Nvidia.\n"
+msgid ""
+"For the best compatibility with your Nvidia hardware, you may want to use "
+"the Nvidia proprietary driver.\n"
+msgstr ""
+"Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez "
+"utiliser le pilote propriétaire Nvidia.\n"
msgid ""
"\n"
@@ -208,13 +276,14 @@ msgid ""
msgstr ""
"\n"
"\n"
-"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source"
+"Sélectionner un pilote graphique ou laisser vide pour installer tous les "
+"pilotes open-source"
msgid "All open-source (default)"
msgstr "Tout open-source (par défaut)"
msgid "Choose which kernels to use or leave blank for default \"{}\""
-msgstr ""
+msgstr "Choisir les noyaux à utiliser ou laisser vide pour \"{}\" par défaut"
msgid "Choose which locale language to use"
msgstr "Choisir la langue locale à utiliser"
@@ -223,22 +292,26 @@ msgid "Choose which locale encoding to use"
msgstr "Choisir quel encodage de paramètres régionaux utiliser"
msgid "Select one of the values shown below: "
-msgstr "Sélectionner l'une des valeurs ci-dessous : "
+msgstr "Sélectionner l'une des valeurs ci-dessous : "
msgid "Select one or more of the options below: "
-msgstr "Sélectionner une ou plusieurs des options ci-dessous : "
+msgstr "Sélectionner une ou plusieurs des options ci-dessous : "
msgid "Adding partition...."
msgstr "Ajout de la partition...."
-msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
-msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides."
+msgid ""
+"You need to enter a valid fs-type in order to continue. See `man parted` for "
+"valid fs-type's."
+msgstr ""
+"Vous devez entrer un type de fs valide pour continuer. Voir `man parted` "
+"pour les types de fs valides."
msgid "Error: Listing profiles on URL \"{}\" resulted in:"
-msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :"
+msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :"
msgid "Error: Could not decode \"{}\" result as JSON:"
-msgstr "Erreur : Impossible de décoder le résultat \"{}\" en tant que JSON :"
+msgstr "Erreur : Impossible de décoder le résultat \"{}\" en tant que JSON :"
msgid "Keyboard layout"
msgstr "Disposition du clavier"
@@ -253,7 +326,7 @@ msgid "Locale encoding"
msgstr "Encodage des paramètres régionaux"
msgid "Drive(s)"
-msgstr "Disques durs"
+msgstr "Disques"
msgid "Disk layout"
msgstr "Disposition du disque"
@@ -286,7 +359,7 @@ msgid "Kernels"
msgstr "Noyaux"
msgid "Additional packages"
-msgstr "Packages supplémentaires"
+msgstr "Paquets supplémentaires"
msgid "Network configuration"
msgstr "Configurer le réseau"
@@ -305,11 +378,12 @@ msgid ""
msgstr ""
"Vous avez décidé d'ignorer la sélection du disque dur\n"
"et vous utiliserez la configuration de disque montée sur {} (expérimental)\n"
-"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette configuration\n"
+"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette "
+"configuration\n"
"Souhaitez-vous continuer ?"
msgid "Re-using partition instance: {}"
-msgstr "Réutilisation de l'instance de partition : {}"
+msgstr "Réutilisation de l'instance de partition : {}"
msgid "Create a new partition"
msgstr "Créer une nouvelle partition"
@@ -330,7 +404,8 @@ msgid "Mark/Unmark a partition as encrypted"
msgstr "Marquer/Démarquer une partition comme chiffrée"
msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
-msgstr "Marquer/Démarquer une partition comme amorçable (automatique pour /boot)"
+msgstr ""
+"Marquer/Démarquer une partition comme amorçable (automatique pour /boot)"
msgid "Set desired filesystem for a partition"
msgstr "Définir le système de fichiers souhaité pour une partition"
@@ -364,48 +439,63 @@ msgid "Suggest partition layout"
msgstr "Suggérer la disposition des partitions"
msgid "Enter a password: "
-msgstr "Entrer un mot de passe : "
+msgstr "Entrer un mot de passe : "
msgid "Enter a encryption password for {}"
msgstr "Entrer un mot de passe de chiffrement pour {}"
msgid "Enter disk encryption password (leave blank for no encryption): "
-msgstr "Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun chiffrement) : "
+msgstr ""
+"Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun "
+"chiffrement) : "
msgid "Create a required super-user with sudo privileges: "
-msgstr "Créer un super-utilisateur requis avec les privilèges sudo : "
+msgstr "Créer un super-utilisateur requis avec les privilèges sudo : "
msgid "Enter root password (leave blank to disable root): "
-msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : "
+msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : "
msgid "Password for user \"{}\": "
msgstr "Mot de passe pour l'utilisateur \"{}\" : "
-msgid "Verifying that additional packages exist (this might take a few seconds)"
-msgstr "Vérifier que des packages supplémentaires existent (cela peut prendre quelques secondes)"
+msgid ""
+"Verifying that additional packages exist (this might take a few seconds)"
+msgstr ""
+"Vérifier que des paquets supplémentaires existent (cela peut prendre "
+"quelques secondes)"
-msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
-msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n"
+msgid ""
+"Would you like to use automatic time synchronization (NTP) with the default "
+"time servers?\n"
+msgstr ""
+"Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec "
+"les serveurs de temps par défaut ?\n"
msgid ""
-"Hardware time and other post-configuration steps might be required in order for NTP to work.\n"
+"Hardware time and other post-configuration steps might be required in order "
+"for NTP to work.\n"
"For more information, please check the Arch wiki"
msgstr ""
-"Le temps matériel et d'autres étapes de post-configuration peuvent être nécessaires pour que NTP fonctionne.\n"
+"Le temps matériel et d'autres étapes de post-configuration peuvent être "
+"nécessaires pour que NTP fonctionne.\n"
"Pour plus d'informations, veuillez consulter le wiki Arch"
msgid "Enter a username to create an additional user (leave blank to skip): "
-msgstr "Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire (laisser vide pour ignorer) : "
+msgstr ""
+"Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire "
+"(laisser vide pour ignorer) : "
msgid "Use ESC to skip\n"
msgstr "Utiliser ESC pour ignorer\n"
msgid ""
"\n"
-" Choose an object from the list, and select one of the available actions for it to execute"
+" Choose an object from the list, and select one of the available actions for "
+"it to execute"
msgstr ""
"\n"
-"Choisir un objet dans la liste et sélectionner l'une des actions disponibles pour qu'il s'exécute"
+"Choisir un objet dans la liste et sélectionner l'une des actions disponibles "
+"pour qu'il s'exécute"
msgid "Cancel"
msgstr "Annuler"
@@ -441,14 +531,21 @@ msgstr ""
"\n"
"Voici la configuration choisie :"
-msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
-msgstr "Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour qu'il se termine."
+msgid ""
+"Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr ""
+"Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour "
+"qu'il se termine."
-msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes les sessions pacman existantes avant d'utiliser archinstall."
+msgid ""
+"Pre-existing pacman lock never exited. Please clean up any existing pacman "
+"sessions before using archinstall."
+msgstr ""
+"Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes "
+"les sessions pacman existantes avant d'utiliser archinstall."
msgid "Choose which optional additional repositories to enable"
-msgstr "Choisir les référentiels supplémentaires en option à activer"
+msgstr "Choisir les dépôts supplémentaires en option à activer"
msgid "Add a user"
msgstr "Ajouter un utilisateur"
@@ -527,7 +624,7 @@ msgid "are you sure you want to use it?"
msgstr "êtes-vous sûr de vouloir l'utiliser ?"
msgid "Optional repositories"
-msgstr "Référentiels supplémentaires"
+msgstr "Dépôts supplémentaires"
msgid "Save configuration"
msgstr "Enregistrer la configuration"
@@ -570,7 +667,7 @@ msgid "Subvolume options"
msgstr "Options de sous-volume"
msgid "Save"
-msgstr "Sauver"
+msgstr "Sauvegarder"
msgid "Subvolume name :"
msgstr "Nom du sous-volume :"
@@ -582,28 +679,35 @@ msgid "Select the desired subvolume options "
msgstr "Sélectionner les options de sous-volume souhaitées "
msgid "Define users with sudo privilege, by username: "
-msgstr "Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : "
+msgstr ""
+"Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : "
msgid "[!] A log file has been created here: {}"
msgstr "[!] Un fichier journal a été créé ici : {}"
msgid "Would you like to use BTRFS subvolumes with a default structure?"
-msgstr "Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par défaut ?"
+msgstr ""
+"Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par "
+"défaut ?"
msgid "Would you like to use BTRFS compression?"
-msgstr "Souhaitez-vous utiliser la compression BTRFS ?"
+msgstr "Souhaitez-vous utiliser la compression BTRFS ?"
msgid "Would you like to create a separate partition for /home?"
-msgstr "Souhaitez-vous créer une partition séparée pour /home ?"
+msgstr "Souhaitez-vous créer une partition séparée pour /home ?"
-msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
-msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n"
+msgid ""
+"The selected drives do not have the minimum capacity required for an "
+"automatic suggestion\n"
+msgstr ""
+"Les disques sélectionnés n'ont pas la capacité minimale requise pour une "
+"suggestion automatique\n"
msgid "Minimum capacity for /home partition: {}GB\n"
-msgstr "Capacité minimale pour la partition /home : {} Go\n"
+msgstr "Capacité minimale pour la partition /home : {} Go\n"
msgid "Minimum capacity for Arch Linux partition: {}GB"
-msgstr "Capacité minimale pour la partition Arch Linux : {} Go"
+msgstr "Capacité minimale pour la partition Arch Linux : {} Go"
msgid "Continue"
msgstr "Poursuivre"
@@ -624,7 +728,9 @@ msgid "No iface specified for manual configuration"
msgstr "Aucun iface spécifié pour la configuration manuelle"
msgid "Manual nic configuration with no auto DHCP requires an IP address"
-msgstr "La configuration manuelle de la carte réseau sans DHCP automatique nécessite une adresse IP"
+msgstr ""
+"La configuration manuelle de la carte réseau sans DHCP automatique nécessite "
+"une adresse IP"
msgid "Add interface"
msgstr "Ajouter une interface"
@@ -644,23 +750,42 @@ msgstr "Configuration manuelle"
msgid "Mark/Unmark a partition as compressed (btrfs only)"
msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)"
-msgid "The password you are using seems to be weak, are you sure you want to use it?"
-msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?"
+msgid ""
+"The password you are using seems to be weak, are you sure you want to use it?"
+msgstr ""
+"Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir "
+"l'utiliser ?"
-msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
-msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway"
+msgid ""
+"Provides a selection of desktop environments and tiling window managers, e."
+"g. gnome, kde, sway"
+msgstr ""
+"Fournit une sélection d'environnements de bureau et de gestionnaires de "
+"fenêtres en mosaïque, par ex. gnome, kde, sway"
msgid "Select your desired desktop environment"
msgstr "Sélectionner l'environnement de bureau souhaité"
-msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
-msgstr "Une installation très basique qui vous permet de personnaliser Arch Linux comme bon vous semble."
+msgid ""
+"A very basic installation that allows you to customize Arch Linux as you see "
+"fit."
+msgstr ""
+"Une installation très basique qui vous permet de personnaliser Arch Linux "
+"comme bon vous semble."
-msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
-msgstr "Fournit une sélection de divers paquets de serveur à installer et à activer, par ex. httpd, nginx, mariadb"
+msgid ""
+"Provides a selection of various server packages to install and enable, e.g. "
+"httpd, nginx, mariadb"
+msgstr ""
+"Fournit une sélection de divers paquets de serveur à installer et à activer, "
+"par ex. httpd, nginx, mariadb"
-msgid "Choose which servers to install, if none then a minimal installation will be done"
-msgstr "Choisir les serveurs à installer, s'il n'y en a pas, une installation minimale sera effectuée"
+msgid ""
+"Choose which servers to install, if none then a minimal installation will be "
+"done"
+msgstr ""
+"Choisir les serveurs à installer, s'il n'y en a pas, une installation "
+"minimale sera effectuée"
msgid "Installs a minimal system as well as xorg and graphics drivers."
msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg."
@@ -668,20 +793,30 @@ msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg."
msgid "Press Enter to continue."
msgstr "Appuyer sur Entrée pour continuer."
-msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer la configuration post-installation ?"
+msgid ""
+"Would you like to chroot into the newly created installation and perform "
+"post-installation configuration?"
+msgstr ""
+"Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer "
+"la configuration post-installation ?"
msgid "Are you sure you want to reset this setting?"
-msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?"
+msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?"
msgid "Select one or more hard drives to use and configure\n"
msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\n"
msgid "Any modifications to the existing setting will reset the disk layout!"
-msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !"
+msgstr ""
+"Toute modification du paramètre existant réinitialisera la disposition du "
+"disque !"
-msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
-msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?"
+msgid ""
+"If you reset the harddrive selection this will also reset the current disk "
+"layout. Are you sure?"
+msgstr ""
+"Si vous réinitialisez la sélection du disque dur, cela réinitialisera "
+"également la disposition actuelle du disque. Êtes-vous sûr ?"
msgid "Save and exit"
msgstr "Sauvegarder et quitter"
@@ -691,7 +826,8 @@ msgid ""
"contains queued partitions, this will remove those, are you sure?"
msgstr ""
"{}\n"
-"contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?"
+"contient des partitions en file d'attente, cela les supprimera, êtes-vous "
+"sûr ?"
msgid "No audio server"
msgstr "Pas de serveur audio"
@@ -722,13 +858,18 @@ msgid "Edit {}: "
msgstr "Modifier {} : "
msgid "Add: "
-msgstr "Ajouter: "
+msgstr "Ajouter : "
msgid "Value: "
-msgstr "Valeur: "
+msgstr "Valeur : "
-msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
-msgstr "Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et utiliser n'importe quelle configuration de lecteur montée sur /mnt (expérimental)"
+msgid ""
+"You can skip selecting a drive and partitioning and use whatever drive-setup "
+"is mounted at /mnt (experimental)"
+msgstr ""
+"Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et "
+"utiliser n'importe quelle configuration de lecteur montée sur /mnt "
+"(expérimental)"
msgid "Select one of the disks or skip and use /mnt as default"
msgstr "Sélectionner l'un des disques ou ignorer et utiliser /mnt par défaut"
@@ -751,8 +892,12 @@ msgstr "Espace libre"
msgid "Bus-type"
msgstr "Type de bus"
-msgid "Either root-password or at least 1 user with sudo privileges must be specified"
-msgstr "Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit être spécifié"
+msgid ""
+"Either root-password or at least 1 user with sudo privileges must be "
+"specified"
+msgstr ""
+"Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit "
+"être spécifié"
msgid "Enter username (leave blank to skip): "
msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : "
@@ -761,7 +906,7 @@ msgid "The username you entered is invalid. Try again"
msgstr "Le nom d'utilisateur que vous avez saisi n'est pas valide. Réessayer"
msgid "Should \"{}\" be a superuser (sudo)?"
-msgstr "\"{}\" devrait-il être un superutilisateur (sudo) ?"
+msgstr "\"{}\" devrait-il être un superutilisateur (sudo) ?"
msgid "Select which partitions to encrypt"
msgstr "Sélectionner les partitions à chiffrer"
@@ -790,31 +935,50 @@ msgstr "Supprimer le sous-volume"
msgid "Configured {} interfaces"
msgstr "Interfaces {} configurées"
-msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation"
+msgid ""
+"This option enables the number of parallel downloads that can occur during "
+"installation"
+msgstr ""
+"Cette option active le nombre de téléchargements parallèles qui peuvent se "
+"produire pendant l'installation"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Saisir le nombre de téléchargements parallèles à activer.\n"
-" (Entrer une valeur comprise entre 1 et {max_downloads})\n"
+" (Entrer une valeur comprise entre 1 et {})\n"
"Note :"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Valeur maximale : {max_downloads} (Autorise {max_downloads} téléchargements parallèles, autorise {max_downloads+1} téléchargements à la fois)"
+msgid ""
+" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads "
+"at a time )"
+msgstr ""
+" - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} "
+"téléchargements à la fois)"
-msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)"
+msgid ""
+" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a "
+"time )"
+msgstr ""
+" - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 "
+"téléchargements à la fois)"
-msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)"
+msgid ""
+" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 "
+"download at a time )"
+msgstr ""
+" - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise "
+"qu'un seul téléchargement à la fois)"
#, python-brace-format
-msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {max_downloads}, ou 0 pour désactiver]"
+msgid ""
+"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to "
+"disable]"
+msgstr ""
+"Entrée invalide ! Réessayer avec une entrée valide [1 pour {max_downloads}, "
+"ou 0 pour désactiver]"
msgid "Parallel Downloads"
msgstr "Téléchargements parallèles"
@@ -831,55 +995,538 @@ msgstr "TAB pour sélectionner"
msgid "[Default value: 0] > "
msgstr "[Valeur par défaut : 0] > "
-msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr "Pour pouvoir utiliser cette traduction, veuillez installer manuellement une police prenant en charge la langue."
+msgid ""
+"To be able to use this translation, please install a font manually that "
+"supports the language."
+msgstr ""
+"Pour pouvoir utiliser cette traduction, veuillez installer manuellement une "
+"police prenant en charge la langue."
msgid "The font should be stored as {}"
msgstr "La police doit être stockée sous {}"
-#, fuzzy
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+"Archinstall nécessite des privilèges root pour s'exécuter. Voir l'aide pour "
+"plus d'informations (--help)."
+
+msgid "Select an execution mode"
+msgstr "Sélectionner un mode d'exécution"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Impossible de récupérer le profil à partir de l'URL spécifiée : {}"
+
+msgid ""
+"Profiles must have unique name, but profile definitions with duplicate name "
+"found: {}"
+msgstr ""
+"Les profils doivent avoir un nom unique, mais des définitions de profil avec "
+"un nom en double ont été trouvées : {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Sélectionner un ou plusieurs périphériques à utiliser et à configurer"
+
+msgid ""
+"If you reset the device selection this will also reset the current disk "
+"layout. Are you sure?"
+msgstr ""
+"Si vous réinitialisez la sélection de périphérique, cela réinitialisera "
+"également la disposition actuelle du disque. Etes-vous sûr ?"
+
+msgid "Existing Partitions"
+msgstr "Partitions existantes"
+
+msgid "Select a partitioning option"
+msgstr "Sélectionner une option de partitionnement"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Entrer le répertoire racine des périphériques montés : "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Capacité minimale pour la partition /home : {} Gio\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Capacité minimale pour la partition Arch Linux : {} Gio"
+
+msgid ""
+"This is a list of pre-programmed profiles_bck, they might make it easier to "
+"install things like desktop environments"
+msgstr ""
+"Ceci est une liste préprogrammée de profiles_bck, ils pourraient faciliter "
+"l'installation de choses comme les environnements de bureau"
+
+msgid "Current profile selection"
+msgstr "Sélection du profil actuel"
+
+msgid "Remove all newly added partitions"
+msgstr "Supprimer toutes les partitions nouvellement ajoutées"
+
+msgid "Assign mountpoint"
+msgstr "Attribuer un point de montage"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Marquer/Démarquer à formater (efface les données)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Marquer/Démarquer comme amorçable"
+
+msgid "Change filesystem"
+msgstr "Changer le système de fichiers"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Marquer/Démarquer comme compressé"
+
+msgid "Set subvolumes"
+msgstr "Définir des sous-volumes"
+
+msgid "Delete partition"
+msgstr "Supprimer la partition"
+
+msgid "Partition"
+msgstr "Partition"
+
+msgid ""
+"This partition is currently encrypted, to format it a filesystem has to be "
+"specified"
+msgstr ""
+"Cette partition est actuellement chiffrée, pour la formater, un système de "
+"fichiers doit être spécifié"
+
+msgid ""
+"Partition mount-points are relative to inside the installation, the boot "
+"would be /boot as an example."
+msgstr ""
+"Les points de montage de partition sont relatifs à l'intérieur de "
+"l'installation, le démarrage serait /boot par exemple."
+
+msgid ""
+"If mountpoint /boot is set, then the partition will also be marked as "
+"bootable."
+msgstr ""
+"Si le point de montage /boot est défini, la partition sera également marquée "
+"comme amorçable."
+
+msgid "Mountpoint: "
+msgstr "Point de montage : "
+
+msgid "Current free sectors on device {}:"
+msgstr "Secteurs actuellement libres sur le périphérique {} :"
+
+msgid "Total sectors: {}"
+msgstr "Total des secteurs : {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Saisir le secteur de début (par défaut : {}) : "
+
+msgid ""
+"Enter the end sector of the partition (percentage or block number, default: "
+"{}): "
+msgstr ""
+"Saisir le secteur de fin de la partition (pourcentage ou numéro de bloc, par "
+"défaut : {}) : "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+"Cela supprimera toutes les partitions nouvellement ajoutées, voulez-vous "
+"continuer ?"
+
+msgid "Partition management: {}"
+msgstr "Gestion des partitions : {}"
+
+msgid "Total length: {}"
+msgstr "Total (taille) : {}"
+
msgid "Encryption type"
-msgstr "Mot de passe de chiffrement"
+msgstr "Type de chiffrement"
msgid "Partitions"
-msgstr ""
+msgstr "Partitions"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Aucun périphérique HSM disponible"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Sélectionner les partitions à chiffrer"
+msgstr "Partitions à chiffrer"
-#, fuzzy
msgid "Select disk encryption option"
-msgstr "Sélectionner la disposition du disque"
+msgstr "Sélectionner l'option de chiffrement du disque"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Sélectionner un périphérique FIDO2 à utiliser pour HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Utiliser une disposition de partition optimale par défaut"
+
+msgid "Manual Partitioning"
+msgstr "Partitionnement manuel"
+
+msgid "Pre-mounted configuration"
+msgstr "Configuration pré-montée"
+
+msgid "Unknown"
+msgstr "Inconnu"
+
+msgid "Partition encryption"
+msgstr "Chiffrement des partitions"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formatage {} dans "
+
+msgid "↠Back"
+msgstr "↠Retour"
+
+msgid "Disk encryption"
+msgstr "Chiffrement du disque"
+
+msgid "Configuration"
+msgstr "Configuration"
+
+msgid "Password"
+msgstr "Mot de passe"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?"
+msgstr "Tous les paramètres seront réinitialisés, êtes-vous sûr ?"
msgid "Back"
+msgstr "Retour"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
msgstr ""
+"Veuillez choisir le greeter (interface de connexion) à installer pour les "
+"profils choisis : {}"
-msgid "Disk encryption"
+msgid "Environment type: {}"
+msgstr "Type d'environnement : {}"
+
+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 ""
+"Le pilote Nvidia propriétaire n'est pas pris en charge par Sway. Il est "
+"probable que vous rencontriez des problèmes, êtes-vous d'accord avec cela ?"
-#, fuzzy
-msgid "Password"
-msgstr "Mot de passe root"
+msgid "Installed packages"
+msgstr "Paquets installés"
-msgid "Partition encryption"
+msgid "Add profile"
+msgstr "Ajouter un profil"
+
+msgid "Edit profile"
+msgstr "Editer le profil"
+
+msgid "Delete profile"
+msgstr "Supprimer le profil"
+
+msgid "Profile name: "
+msgstr "Nom de profil : "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+"Le nom de profil que vous avez saisi est déjà utilisé. Essayer à nouveau"
+
+msgid ""
+"Packages to be install with this profile (space separated, leave blank to "
+"skip): "
+msgstr ""
+"Saisir les paquets à installer avec ce profil (séparés par des espaces, vide "
+"pour ignorer) : "
+
+msgid ""
+"Services to be enabled with this profile (space separated, leave blank to "
+"skip): "
+msgstr ""
+"Saisir les services à activer avec ce profil (séparés par des espaces, vide "
+"pour ignorer) : "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Ce profil doit-il être activé pour l'installation ?"
+
+msgid "Create your own"
+msgstr "Créer le votre"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"Sélectionner un pilote graphique ou laisser vide pour installer tous les "
+"pilotes open source"
+
+msgid ""
+"Sway needs access to your seat (collection of hardware devices i.e. "
+"keyboard, mouse, etc)"
+msgstr ""
+"Sway a besoin d'accéder à votre espace (ensemble de périphériques "
+"matériels : clavier, souris, etc.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Choisir une option pour permettre à Sway d'accéder à votre matériel"
+
+msgid "Graphics driver"
+msgstr "Pilote graphique"
+
+msgid "Greeter"
+msgstr "Greeter (interface de connexion)"
+
+msgid "Please chose which greeter to install"
+msgstr "Veuillez choisir le greeter (interface de connexion) à installer"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Il s'agit d'une liste préprogrammée de default_profiles par défaut"
+
+msgid "Disk configuration"
+msgstr "Configuration du disque"
+
+msgid "Profiles"
+msgstr "Profils"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+"Recherche des répertoires possibles pour enregistrer les fichiers de "
+"configuration..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr ""
+"Sélectionner le répertoire (ou les répertoires) pour enregistrer les "
+"fichiers de configuration"
+
+msgid "Add a custom mirror"
+msgstr "Ajouter un miroir personnalisé"
+
+msgid "Change custom mirror"
+msgstr "Changer le miroir personnalisé"
+
+msgid "Delete custom mirror"
+msgstr "Supprimer le miroir personnalisé"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Entrer le nom (laisser vide pour ignorer) : "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Entrer l'URL (laisser vide pour ignorer) : "
+
+msgid "Select signature check option"
+msgstr "Sélectionner l'option de vérification de signature"
+
+msgid "Select signature option"
+msgstr "Sélectionner l'option de signature"
+
+msgid "Custom mirrors"
+msgstr "Miroirs personnalisés"
+
+msgid "Defined"
+msgstr "Défini"
+
+msgid "Save user configuration (including disk layout)"
+msgstr ""
+"Enregistrer la configuration utilisateur (y compris la disposition du disque)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion "
+"enabled)\n"
+"Save directory: "
+msgstr ""
+"Saisir un répertoire pour la/les configuration(s) à enregistrer (complétion "
+"par tabulation activée)\n"
+"Entrer le nom du répertoire de sauvegarde : "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Voulez-vous enregistrer {} fichier(s) de configuration à l'emplacement "
+"suivant ?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Enregistrement de {} fichiers de configuration dans {}"
+
+msgid "Mirrors"
+msgstr "Miroirs"
+
+msgid "Mirror regions"
+msgstr "Régions miroir"
+
+msgid ""
+" - Maximum value : {} ( Allows {} parallel downloads, allows "
+"{max_downloads+1} downloads at a time )"
+msgstr ""
+" - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise "
+"{max_downloads+1} téléchargements à la fois)"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+"Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour "
+"désactiver]"
+
+msgid "Locales"
+msgstr "Paramètres régionaux"
+
+msgid ""
+"Use NetworkManager (necessary to configure internet graphically in GNOME and "
+"KDE)"
+msgstr ""
+"Utiliser NetworkManager (nécessaire pour configurer graphiquement internet "
+"dans GNOME et KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Total (taille) : {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+"Toutes les valeurs saisies peuvent être saccompagnées par une unité : B, KB, "
+"KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+"Si aucune unité n'est fournie, la valeur est interprétée comme des secteurs\""
+
+msgid "Enter start (default: sector {}): "
+msgstr "Saisir le secteur de début (par défaut : secteur {}) : "
+
+msgid "Enter end (default: {}): "
+msgstr "Saisir le secteur de fin (par défaut : secteur {}) : "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+"Impossible de déterminer les appareils fido2. Est-ce que libfido2 est "
+"installé ?"
+
+msgid "Path"
+msgstr "Chemin"
+
+msgid "Manufacturer"
+msgstr "Fabricant"
+
+msgid "Product"
+msgstr "Produit"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configuration invalide : {error}"
+
+msgid "Type"
+msgstr "Type"
+
+msgid ""
+"This option enables the number of parallel downloads that can occur during "
+"package downloads"
+msgstr ""
+"Cette option active le nombre de téléchargements parallèles pouvant avoir "
+"lieu lors des téléchargements des paquets"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Saisir le nombre de téléchargements parallèles à activer.\n"
+"\n"
+"Note :\n"
+
+msgid ""
+" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+" - Valeur maximale recommandée : {} (Autorise {} téléchargements parallèles "
+"à la fois)"
+
+msgid ""
+" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 "
+"download at a time )\n"
+msgstr ""
+" - Désactiver/Par défaut : 0 (Désactive le téléchargement parallèle, "
+"autorise un seul téléchargement à la fois)\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+"Entrée invalide ! Réessayer avec une entrée valide [ou 0 pour désactiver]"
+
+msgid ""
+"Hyprland needs access to your seat (collection of hardware devices i.e. "
+"keyboard, mouse, etc)"
+msgstr ""
+"Hyprland a besoin d'accéder à votre espace (ensemble de périphériques "
+"matériels, par exemple clavier, souris, etc)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Choisir une option pour donner à Hyprland l'accès à votre matériel"
+
+msgid ""
+"All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+"Toutes les valeurs saisies peuvent être accompagnées par une unité : %, B, "
+"KB, KiB, MB, MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Souhaitez-vous utiliser des images de noyau unifiées ?"
+
+msgid "Unified kernel images"
+msgstr "Images du noyau unifiées"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+"En attente de la fin de la synchronisation de l'heure (timedatectl show)."
+
+msgid ""
+"Time syncronization not completing, while you wait - check the docs for "
+"workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+"La synchronisation de l'heure ne se termine pas, veuillez consulter la "
+"documentation afin de trouver des solutions de contournement : https://"
+"archinstall.readthedocs.io/"
+
+msgid ""
+"Skipping waiting for automatic time sync (this can cause issues if time is "
+"out of sync during installation)"
+msgstr ""
+"Ignorer l'attente de la synchronisation automatique de l'heure (cela peut "
+"entraîner des problèmes si l'heure n'est pas synchronisée lors de "
+"l'installation)"
+
+msgid ""
+"Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+"En attente de la fin de la synchronisation du trousseau de clés d'Arch Linux "
+"(archlinux-keyring-wkd-sync)."
+
+msgid "Selected profiles: "
+msgstr "Profils sélectionnés : "
+
+msgid ""
+"Time synchronization not completing, while you wait - check the docs for "
+"workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+"La synchronisation de l'heure ne se termine pas, veuillez consulter la "
+"documentation afin de trouver des solutions de contournement : https://"
+"archinstall.readthedocs.io/"
+
+msgid "Mark/Unmark as nodatacow"
+msgstr "Marquer/Démarquer comme nodatacow"
+
+msgid "Would you like to use compression or disable CoW?"
msgstr ""
+"Souhaitez-vous utiliser la compression ou désactiver la copie à l'écriture "
+"(CoW ) ?"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Entrer le secteur de début (pourcentage ou numéro de bloc, par défaut : {}) : "
+msgid "Use compression"
+msgstr "Utiliser la compression"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Entrer le secteur de fin de la partition (pourcentage ou numéro de bloc, ex : {}) : "
+msgid "Disable Copy-on-Write"
+msgstr "Désactiver la copie à l'écriture (CoW)"
#, python-brace-format
#~ msgid "Edit {origkey} :"
diff --git a/archinstall/locales/he/LC_MESSAGES/base.mo b/archinstall/locales/he/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..5b554862
--- /dev/null
+++ b/archinstall/locales/he/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/he/LC_MESSAGES/base.po b/archinstall/locales/he/LC_MESSAGES/base.po
new file mode 100644
index 00000000..b1694404
--- /dev/null
+++ b/archinstall/locales/he/LC_MESSAGES/base.po
@@ -0,0 +1,1257 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
+"Language-Team: \n"
+"Language: he\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.4.2\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] ×›×ן נוצר קובץ היומן: {} {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " × × ×œ×”×’×™×© דיווח על הבעיה ×”×–×ת (ו×ת הקובץ) דרך https://github.com/archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "לבטל ×ת התהליך?"
+
+msgid "And one more time for verification: "
+msgstr "×•×¤×¢× × ×•×¡×¤×ª ל×ימות: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "להשתמש בשטח החלפה ב־zram?"
+
+msgid "Desired hostname for the installation: "
+msgstr "×©× ×ž×רח רצוי להתקנה: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "×©× ×œ×ž×©×ª×ž×© העל הנחוץ ×¢× ×”×¨×©×ות על: "
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "×ž×©×ª×ž×©×™× × ×•×¡×¤×™× ×œ×”×ª×§× ×” (ריק משמעו ×ין עוד משתמשי×): "
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "×–×” ×מור להיות משתמש על (sudoer)?"
+
+msgid "Select a timezone"
+msgstr "בחירת ×זור זמן"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "להשתמש ב־GRUB כמנהל טעינה על פני systemd-boot?"
+
+msgid "Choose a bootloader"
+msgstr "× × ×œ×‘×—×•×¨ מנהל טעינה"
+
+msgid "Choose an audio server"
+msgstr "× × ×œ×‘×—×•×¨ שרת שמע"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "רק חבילות כגוןbase,†base-devel,†linux,†linux-firmware,†efibootmgr וחבילות פרופיל כרשות מותקנות."
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr "כדי שיותקן דפדפן כגון firefox ×ו chromium ×פשר לציין ×–×ת בבקשה הב××”."
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "× × ×œ×›×ª×•×‘ חבילות נוספות להתקנה (להפריד ברווחי×, להש×יר ריק כדי לדלג): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr "העתקת הגדרות הרשת מה־ISO להתקנה"
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "להשתמש ב־NetworkManager (חיוני להגדרת ×”×ינטרנט ×¢× ×›×œ×™× ×—×–×•×ª×™×™× ×‘Ö¾GNOME וב־KDE)"
+
+msgid "Select one network interface to configure"
+msgstr "× × ×œ×‘×—×•×¨ מנשק רשת להגדרה"
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr "× × ×œ×‘×—×•×¨ ××™×–×” מצב להגדרה עבור „{}“ ×ו לדלג כדי להשתמש במצב ברירת המחדל „{}“"
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "× × ×œ×ž×œ× IP ורשת־משנה (ס×בנט) עבור {} (למשל: 192.168.0.5/24): "
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "× × ×œ×ž×œ× ×ת כתובת ×”Ö¾IP של שער הגישה (ר×וטר) ×ו להש×יר ריק כדי ×œ× ×œ×”×’×“×™×¨: "
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr "× × ×œ×ž×œ× ×ת שרתי ×”Ö¾DNS שלך (להפריד ברווחי×, להש×יר ריק ×× ×ין): "
+
+msgid "Select which filesystem your main partition should use"
+msgstr "× × ×œ×‘×—×•×¨ ב×יזו מערכת ×§×‘×¦×™× ×¦×¨×™×›×” להשתמש המחיצה הר×שית שלך"
+
+msgid "Current partition layout"
+msgstr "פריסת מחיצות נוכחית"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"× × ×œ×‘×—×•×¨ מה לעשות ×¢×‬\n"
+"‫{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "× × ×œ×¦×™×™×Ÿ ×ת סוג מערכת ×”×§×‘×¦×™× ×”×¨×¦×•×™×” במחיצה"
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "× × ×œ×ž×œ× ×ת ×ž×™×§×•× ×”×”×ª×—×œ×” (ביחידות של partedâ€: s,†GB, %, וכו׳ ; ברירת מחדל: {}): "
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "× × ×œ×ž×œ× ×ת ×ž×™×§×•× ×”×¡×•×£ (ביחידות של partedâ€: s,†GB, %, וכו׳ ; למשל: {}): "
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "{} מכיל מחיצות ממתינות, פעולה זו תסיר ×ותן, להמשיך?"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}‬\n"
+"\n"
+"‫בחירה לפי מפתח ×ילו מחיצות למחוק"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}‬\n"
+"\n"
+"‫בחירה לפי מפתח ×ילו מחיצות לעגן ו×יפה"
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * נקודות העגינה של המחיצה הן יחסיות למערכת ההתקנה, מחיצת הטעינה למשל תהיה ‎/boot."
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr "× × ×œ×‘×—×•×¨ להיכן לעגן ×ת המחיצה (להש×יר ריק כדי להסיר ×ת נקודת ×”×¢×’×™× ×”): "
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"‫{}‬‬\n"
+"\n"
+"â€«× × ×œ×‘×—×•×¨ ×יזו מחיצה לסמן לפרמוט"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"‫{}‬‬‬\n"
+"\n"
+"â€«â€«× × ×œ×‘×—×•×¨ ×יזו מחיצה לסמן להצפנה"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"‫{}‬‬‬\n"
+"\n"
+"â€«× × ×œ×‘×—×•×¨ ×יזו מחיצה לסמן כזמינה לטעינה"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"‫{}‬\n"
+"\n"
+"â€«× × ×œ×‘×—×•×¨ על ×יזו מחיצה להגדיר מערכת קבצי×"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "× × ×œ×¦×™×™×Ÿ סוג מערכת ×§×‘×¦×™× ×¨×¦×•×™×” למחיצה: "
+
+msgid "Archinstall language"
+msgstr "השפה של Archinstall"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr "למחוק ×ת כל ×”×›×•× × ×™× ×”× ×‘×—×¨×™× ×•×œ×”×©×ª×ž×© בפריסת מחיצות כברירת מחדל על בסיס מ×מץ מיטבי"
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr "לבחור מה לעשות ×¢× ×›×œ כונן בנפרד (×¢× ×ופן השימוש במחיצה בסוף)"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "× × ×œ×‘×—×•×¨ מה לעשות ×¢× ×”×ª×§× ×™ הבלוק הנבחרי×"
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr "×–×ת רשימה של ×¤×¨×•×¤×™×œ×™× ×©× ×›×ª×‘×• מר×ש, ×”× ×¢×©×•×™×™× ×œ×”×§×œ על התקנת ×“×‘×¨×™× ×›×ž×• סביבות שולחן עבודה"
+
+msgid "Select keyboard layout"
+msgstr "× × ×œ×‘×—×•×¨ פריסת מקלדת"
+
+msgid "Select one of the regions to download packages from"
+msgstr "× × ×œ×‘×—×•×¨ ×ת ×חר מה××–×•×¨×™× ×œ×”×•×¨×™×“ ממנו חבילות"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "× × ×œ×‘×—×•×¨ כונן קשיח ×חד ×ו יותר לשימוש והגדרה"
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr "לת×ימות המיטבית ×¢× ×—×•×ž×¨×ª ×”Ö¾AMD שלך, כד××™ להשתמש ×ו ב×פשרות של קוד פתוח לחלוטין ×ו ב־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 "לת×ימות המיטבית ×¢× ×—×•×ž×¨×ª ×”×ינטל שלך, כד××™ להשתמש ×ו ב×פשרות של קוד פתוח לחלוטין ×ו ב×ינטל.‬\n"
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr "לת×ימות המיטבית ×¢× ×—×•×ž×¨×ª ×”Ö¾Nvidia שלך, כד××™ להשתמש במנהל ההתקן הקנייני של Nvidia.‬\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"‫‬‬\n"
+"â€«â€«× × ×œ×‘×—×•×¨ מנהל התקן גרפי ×ו להש×יר ריק כדי להתקין מנהלי ×”×ª×§× ×™× ×‘×§×•×“ פתוח לגמרי"
+
+msgid "All open-source (default)"
+msgstr "הכול בקוד פתוח (ברירת מחדל)"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "× × ×œ×‘×—×•×¨ ×ילו ליבות להשתמש ×ו להש×יר ריק לברירת המחדל „{}â€"
+
+msgid "Choose which locale language to use"
+msgstr "× × ×œ×‘×—×•×¨ ב×יזו שפה של הגדרה ×זורית להשתמש"
+
+msgid "Choose which locale encoding to use"
+msgstr "× × ×œ×‘×—×•×¨ ××™×–×” קידוד של הגדרה ×זורית להשתמש"
+
+msgid "Select one of the values shown below: "
+msgstr "× × ×œ×‘×—×•×¨ ×ת ×חד ×ž×”×¢×¨×›×™× ×©×ž×•×¤×™×¢ להלן: "
+
+msgid "Select one or more of the options below: "
+msgstr "יש לבחור ב×פשרות ×חת ×ו יותר מ×לו שלהלן: "
+
+msgid "Adding partition...."
+msgstr "נוספת מחיצה…"
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr "יש ×œ×ž×œ× ×¡×•×’ מערכת ×§×‘×¦×™× ×ª×§×¤×” כדי להמשיך. ניתן לעיין ב־`man parted` לקבלת רשימת סוגי מערכת ×”×§×‘×¦×™× ×”×ª×§×¤×•×ª."
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "שגי××”: הצגת ×”×¤×¨×•×¤×™×œ×™× ×‘×›×ª×•×‘×ª „{}“ הניבה:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "×œ× × ×™×ª×Ÿ לפענח ×ת התוצ××” „{}“ ×›Ö¾JSON:"
+
+msgid "Keyboard layout"
+msgstr "פריסת מקלדת"
+
+msgid "Mirror region"
+msgstr "×זור ×תר מר××”"
+
+msgid "Locale language"
+msgstr "שפת ההגדרה ×”×זורית"
+
+msgid "Locale encoding"
+msgstr "קידוד ההגדרה ×”×זורית"
+
+msgid "Drive(s)"
+msgstr "כונני×"
+
+msgid "Disk layout"
+msgstr "פריסת כונני×"
+
+msgid "Encryption password"
+msgstr "סיסמת הצפנה"
+
+msgid "Swap"
+msgstr "שטח החלפה"
+
+msgid "Bootloader"
+msgstr "מנהל טעינה"
+
+msgid "Root password"
+msgstr "סיסמת root (משתמש עליון)"
+
+msgid "Superuser account"
+msgstr "חשבון משתמש־על"
+
+msgid "User account"
+msgstr "חשבון משתמש"
+
+msgid "Profile"
+msgstr "פרופיל"
+
+msgid "Audio"
+msgstr "שמע"
+
+msgid "Kernels"
+msgstr "ליבות"
+
+msgid "Additional packages"
+msgstr "חבילות נוספות"
+
+msgid "Network configuration"
+msgstr "הגדרות רשת"
+
+msgid "Automatic time sync (NTP)"
+msgstr "סנכרון זמן ×וטומטי (NTP)"
+
+msgid "Install ({} config(s) missing)"
+msgstr "התקנה ({} הגדרות חסרות)"
+
+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 ""
+"החלטת לוותר על בחירת ×›×•× × ×™× ×§×©×™×—×™×\n"
+"‬\n"
+"‫ולהשתמש בתצורת ×”×›×•× × ×™× ×©×ž×¢×•×’× ×ª על {} כמו ×©×”×™× (ניסיוני)\n"
+"‬\n"
+"‫×זהרה: Archinstall won't check the suitability of this setup\n"
+"‬\n"
+"â€â€«Do you wish to continue?"
+
+msgid "Re-using partition instance: {}"
+msgstr "שימוש מחדש בעותק של מחיצה: {}"
+
+msgid "Create a new partition"
+msgstr "יצירת מחיצה חדשה"
+
+msgid "Delete a partition"
+msgstr "מחיקת מחיצה"
+
+msgid "Clear/Delete all partitions"
+msgstr "פינוי/מחיקה של כל המחיצות"
+
+msgid "Assign mount-point for a partition"
+msgstr "הקצ×ת נקודת ×¢×’×™× ×” למחיצה"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "סימון/ביטול סימון מחיצה לפרמוט (מחיקה מוחלטת של הנתוני×)"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "סימון/ביטול סימון מחיצה כמוצפנת"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "סימון/ביטול סימון מחיצה כרש×ית טעינה (×וטומטית ל־‎/boot)"
+
+msgid "Set desired filesystem for a partition"
+msgstr "× × ×œ×”×’×“×™×¨ מערכת ×§×‘×¦×™× ×¨×¦×•×™×” למחיצה"
+
+msgid "Abort"
+msgstr "ביטול"
+
+msgid "Hostname"
+msgstr "×©× ×ž×רח"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "×œ× ×ž×•×’×“×¨, ×œ× ×–×ž×™×Ÿ למעט במקרה של התקנה ידנית"
+
+msgid "Timezone"
+msgstr "×זור זמן"
+
+msgid "Set/Modify the below options"
+msgstr "הגדרת/שינוי ×”×פשרויות הב×ות"
+
+msgid "Install"
+msgstr "התקנה"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"להשתמש ב־ESC כדי לצ×ת‬\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "הצעת פריסת מחיצות"
+
+msgid "Enter a password: "
+msgstr "× × ×œ×ž×œ× ×¡×™×¡×ž×”: "
+
+msgid "Enter a encryption password for {}"
+msgstr "× × ×œ×ž×œ× ×¡×™×¡×ž×ª הצפנה עבור {}"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "× × ×œ×ž×œ× ×¡×™×¡×ž×” להצפנת הכונן (להש×יר ריק כדי ×œ× ×œ×”×’×“×™×¨ הצפנה): "
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "יצירת משתמש על נחוץ ×¢× ×”×¨×©×ות sudo: "
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "× × ×œ×ž×œ× ×¡×™×¡×ž×” למשתמש העליון (יש להש×יר ריק כדי להשבית ×ת root): "
+
+msgid "Password for user \"{}\": "
+msgstr "סיסמה למשתמש „{}“: "
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr "מתבצע ×•×™×“×•× ×©×—×‘×™×œ×•×ª נוספות קיימות (יכול לקחת כמה שניות)"
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr "להשתמש בסנכרון שעון ×וטומטי (NTP) מול שרתי התזמון כברירת מחדל?‬\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 ""
+"שעון חומרה ×•×¦×¢×“×™× × ×•×¡×¤×™× ×œ×חר ההתקנה כנר××” יהיו × ×—×•×¦×™× ×›×“×™ שה־NTP יעבוד.‬\n"
+"‫למידע נוסף, × × ×œ×¤× ×•×ª לוויקי של Arch"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "× × ×œ×ž×œ× ×©× ×ž×©×ª×ž×© כדי ליצור משתמש נוסף (ריק כדי לדלג): "
+
+msgid "Use ESC to skip\n"
+msgstr "יש להשתמש ב־ESC כדי לדלג‬\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+"\n"
+"â€«× × ×œ×‘×—×•×¨ ×¢×¦× ×ž×”×¨×©×™×ž×” ולבחור ב×חת מה×פשרויות הזמינות עבורו כדי להפעיל ×ותו"
+
+msgid "Cancel"
+msgstr "הסגה"
+
+msgid "Confirm and exit"
+msgstr "×ישור ויצי××”"
+
+msgid "Add"
+msgstr "הוספה"
+
+msgid "Copy"
+msgstr "העתקה"
+
+msgid "Edit"
+msgstr "עריכה"
+
+msgid "Delete"
+msgstr "מחיקה"
+
+msgid "Select an action for '{}'"
+msgstr "× × ×œ×‘×—×•×¨ פעולה עבור ‚{}’"
+
+msgid "Copy to new key:"
+msgstr "העתקה למפתח חדש:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "סוג מת×× ×ª×§×©×•×¨×ª ×œ× ×™×“×•×¢: {}. ×”×¢×¨×›×™× ×”××¤×©×¨×™×™× ×”× {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"‫ז×ת ההגדרה שבחרת:"
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "‫Pacman כבר פועל, נמתין למשך 10 דקות לכל היותר עד לסיומו."
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr "נעילת ×”Ö¾pacman הקודמת ×ž×¢×•×œ× ×œ× × ×¡×’×¨×”. × × ×œ× ×§×•×ª הפעלות קיימות של pacman ×‘×˜×¨× ×”×¤×¢×œ×ª archinstall."
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "× × ×œ×‘×—×•×¨ ×ילו מ××’×¨×™× × ×•×¡×¤×™× ×œ×”×¤×¢×™×œ כרשות"
+
+msgid "Add a user"
+msgstr "הוספת משתמש"
+
+msgid "Change password"
+msgstr "החלפת סיסמה"
+
+msgid "Promote/Demote user"
+msgstr "קידו×/הסגת משתמש"
+
+msgid "Delete User"
+msgstr "מחיקת משתמש"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"‫הגדרת משתמש חדש‬\n"
+
+msgid "User Name : "
+msgstr "×©× ×ž×©×ª×ž×© : "
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "×”×× {} ×מור לקבל הרש×ות על (sudoer)?"
+
+msgid "Define users with sudo privilege: "
+msgstr "הגדרת ×ž×©×ª×ž×©×™× ×¢× ×”×¨×©×ת sudo: "
+
+msgid "No network configuration"
+msgstr "×ין הגדרות רשת"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "הגדרת ×ª×ªÖ¾×›×¨×›×™× ×¨×¦×•×™×™× ×‘×ž×—×™×¦×ª BTRFS"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}‬\n"
+"\n"
+"â€«× × ×œ×‘×—×•×¨ תחת ×יזו מחיצה להגדיר תת־כרכי×"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "ניהול תת־כרכי BTRFS למחיצה הנוכחית"
+
+msgid "No configuration"
+msgstr "×ין הגדרה"
+
+msgid "Save user configuration"
+msgstr "שמירת הגדרת משתמש"
+
+msgid "Save user credentials"
+msgstr "שמירת פרטי משתמש"
+
+msgid "Save disk layout"
+msgstr "שמירת פריסת כונן"
+
+msgid "Save all"
+msgstr "לשמור הכול"
+
+msgid "Choose which configuration to save"
+msgstr "× × ×œ×‘×—×•×¨ ×ילו הגדרות לשמור"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "× × ×œ×ž×œ× ×ת התיקייה להגדרות לשמירה: "
+
+msgid "Not a valid directory: {}"
+msgstr "××™× ×” תיקייה תקפה: {}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "הסיסמה שבחרת נר×ית חלשה,"
+
+msgid "are you sure you want to use it?"
+msgstr "בכל ×–×ת להשתמש בה?"
+
+msgid "Optional repositories"
+msgstr "מ×גרי רשות"
+
+msgid "Save configuration"
+msgstr "שמירת הגדרה"
+
+msgid "Missing configurations:\n"
+msgstr ""
+"הגדרות חסרות:\n"
+"‬\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "יש לציין ×ו סיסמה למשתמש העליון (root) ×ו לפחות משתמש ×חד ×¢× ×”×¨×©×ות על (sudo)"
+
+msgid "Manage superuser accounts: "
+msgstr "ניהול ×ž×©×ª×ž×©×™× ×¢× ×”×¨×©×ות על: "
+
+msgid "Manage ordinary user accounts: "
+msgstr "ניהול חשבונות ×ž×©×ª×ž×©×™× ×¨×’×™×œ×™×: "
+
+msgid " Subvolume :{:16}"
+msgstr " תת־כרך :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " מעוגן תחת {:16}"
+
+msgid " with option {}"
+msgstr " ×¢× ×”×פשרות {}"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+"‫‫‫‫ × × ×œ×ž×œ× ×ת ×”×¢×¨×›×™× ×”×¨×¦×•×™×™× ×œ×ª×ªÖ¾×›×¨×š חדש ‬\n"
+
+msgid "Subvolume name "
+msgstr "×©× ×ª×ªÖ¾×›×¨×š "
+
+msgid "Subvolume mountpoint"
+msgstr "נק׳ עיגון תת־כרך"
+
+msgid "Subvolume options"
+msgstr "×פשרויות תת־כרך"
+
+msgid "Save"
+msgstr "שמירה"
+
+msgid "Subvolume name :"
+msgstr "×©× ×ª×ªÖ¾×›×¨×š :"
+
+msgid "Select a mount point :"
+msgstr "× × ×œ×‘×—×•×¨ נק׳ ×¢×’×™× ×” :"
+
+msgid "Select the desired subvolume options "
+msgstr "× × ×œ×‘×—×•×¨ ×ת ×פשרויות התת־כרך הרצויות "
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "הגדרת ×ž×©×ª×ž×©×™× ×¢× ×”×¨×©×ת על (sudo), לפי ×©× ×ž×©×ª×ž×©: "
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] ×›×ן נוצר קובץ יומן: {}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "להשתמש ×‘×ª×ªÖ¾×›×¨×›×™× ×©×œ BTRFS במבנה ברירת המחדל?"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "להשתמש בדחיסה של BTRFS?"
+
+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"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "הקיבולת המזערית למחיצת ‎/home:†{}ג״ב\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "הקיבולת המזערית למחיצת ‎/home:†{}ג״ב"
+
+msgid "Continue"
+msgstr "המשך"
+
+msgid "yes"
+msgstr "כן"
+
+msgid "no"
+msgstr "ל×"
+
+msgid "set: {}"
+msgstr "הוגדר: {}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "רשומת ההגדרה הידנית חייבת להיות רשימה"
+
+msgid "No iface specified for manual configuration"
+msgstr "×œ× ×¦×•×™×Ÿ iface להגדרה ידנית"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr "הגדרת מת×× ×ª×§×©×•×¨×ª ×œ×œ× DHCP ×וטומטי דורש כתובת IP"
+
+msgid "Add interface"
+msgstr "הוספת מנשק"
+
+msgid "Edit interface"
+msgstr "עריכת מנשק"
+
+msgid "Delete interface"
+msgstr "מחיקת מנשק"
+
+msgid "Select interface to add"
+msgstr "× × ×œ×‘×—×•×¨ מנשק להוספה"
+
+msgid "Manual configuration"
+msgstr "הגדרה ידנית"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "סימון/ביטול סימון מחיצה כדחוסה (btrfs בלבד)"
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "נר××” שהסיסמה שבחרת חלשה, בכל ×–×ת להשתמש בה?"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr "מספק מבחר סביבות שולחן עבודה ומנהלי ריצוף חלונות, למשל: gnome,†kde,†sway"
+
+msgid "Select your desired desktop environment"
+msgstr "× × ×œ×‘×—×•×¨ ×ת סביבת שולחן העבודה הרצויה לך"
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr "התקנה בסיסית מ×וד שמ×פשרת לך להת××™× ×ת Arch Linux בדיוק לדרך שנוחה לך."
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr "מספק מבחר חבילות שרת להתקנה ולהפעלה, למשל: httpd,†nginx,†mariadb"
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr "× × ×œ×‘×—×•×¨ ×ילו ×©×¨×ª×™× ×œ×”×ª×§×™×Ÿ, ×× ×ין ××– תבוצע התקנה מזערית"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr "מתקין מערכת מזערית בנוסף ל־xorg ולמנהלי ×”×ª×§× ×™× ×’×¨×¤×™×™×."
+
+msgid "Press Enter to continue."
+msgstr "× × ×œ×œ×—×•×¥ על Enter כדי להמשיך."
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr "×”×× ×œ×”×™×›× ×¡ להתקנה החדשה שיצרת ×¢× chroot (העמסת סביבה) לביצוע הגדרות של×חר התקנה?"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "ל×פס ×ת ההגדרה ×”×–×ת?"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "× × ×œ×‘×—×•×¨ כונן ×חד ×ו ×ו יותר לשימוש ולהגדרה‬\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "כל שינוי ×©×”×•× ×œ×”×’×“×¨×” הקיימת ×™×פס ×ת פריסת הכונני×!"
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr "×יפוס בחירת ×”×›×•× × ×™× ×™×פס ×’× ×ת פריסת ×”×›×•× × ×™× ×”× ×•×›×—×™×ª. להמשיך?"
+
+msgid "Save and exit"
+msgstr "לשמור ולצ×ת"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"‫{}‬\n"
+"‫‬‫מכיל מחיצות שממתינות בתור, הפעולה ×”×–×ת תסיר ×ותן, להמשיך?"
+
+msgid "No audio server"
+msgstr "×ין שרת שמע"
+
+msgid "(default)"
+msgstr "(ברירת מחדל)"
+
+msgid "Use ESC to skip"
+msgstr "יש להשתמש ב־ESC כדי לדלג"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+"יש להשתמש ב־CTRL+C כדי ל×פס ×ת הבחירה הנוכחית\n"
+"‬\n"
+"â€â€«\n"
+"‬\n"
+
+msgid "Copy to: "
+msgstr "העתקה ×ל: "
+
+msgid "Edit: "
+msgstr "עריכה: "
+
+msgid "Key: "
+msgstr "מפתח: "
+
+msgid "Edit {}: "
+msgstr "עריכת {}: "
+
+msgid "Add: "
+msgstr "הוספה: "
+
+msgid "Value: "
+msgstr "ערך: "
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr "×פשר לדלג על בחירת כונן וחלוקה למחיצות ולהשתמש בתצורת ×”×›×•× × ×™× ×©×ž×¢×•×’× ×ª תחת ‎/mnt כפי ×©×”×™× (ניסיוני)"
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "× × ×œ×‘×—×•×¨ ×חד ×ž×”×›×•× × ×™× ×ו לדלג ולהשתמש ב־‎/mnt כברירת מחדל"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "× × ×œ×‘×—×•×¨ ×ילו מחיצות לסמן לפרמוט:"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "להשתמש ב־HSM לשחרור כונן מוצפן"
+
+msgid "Device"
+msgstr "התקן"
+
+msgid "Size"
+msgstr "גודל"
+
+msgid "Free space"
+msgstr "×ž×§×•× ×¤× ×•×™"
+
+msgid "Bus-type"
+msgstr "סוג ×פיק"
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr "יש לציין ×ו סיסמה למשתמש העליון (root) ×ו לפחות משתמש ×חד ×¢× ×”×¨×©×ות על (sudo)"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "× × ×œ×ž×œ× ×©× ×ž×©×ª×ž×© (להש×יר ריק כדי לדלג): "
+
+msgid "The username you entered is invalid. Try again"
+msgstr "×©× ×”×ž×©×ª×ž×© שמיל×ת שגוי. × × ×œ× ×¡×•×ª שוב"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "×”×× ×œÖ¾â€ž{}“ ×מורות להיות הרש×ות על (sudo)?"
+
+msgid "Select which partitions to encrypt"
+msgstr "× × ×œ×‘×—×•×¨ ×יזו מחיצה להצפין"
+
+msgid "very weak"
+msgstr "חלשה מ×וד"
+
+msgid "weak"
+msgstr "חלשה"
+
+msgid "moderate"
+msgstr "מתונה"
+
+msgid "strong"
+msgstr "חזקה"
+
+msgid "Add subvolume"
+msgstr "הוספת תת־כרך"
+
+msgid "Edit subvolume"
+msgstr "עריכת תת־כרך"
+
+msgid "Delete subvolume"
+msgstr "מחיקת תת־כרך"
+
+msgid "Configured {} interfaces"
+msgstr "הוגדרו {} מנשקי×"
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr "×”×פשרות ×”×–×ת מ×פשרת מספר הורדות במקביל שיכולות להתרחש במהלך התקנה"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"× × ×œ×ž×œ× ×ת מספר ההורדות המקביליות שתהיינה פעילות.‬\n"
+"‫ (×מור להיות ערך בין 1 ל־{})‬\n"
+"‫הערה:"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - ערך מרבי : {} ( מ×פשר {} הורדות במקביל, מ×פשר {} הורדות בבת ×חת )"
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr " - ערך מזערי : 1 ( מ×פשר כל הורדות במקביל, מ×פשר שתי הורדות בו־זמנית )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr " - השבתה/ברירת מחדל : 0 ( השבתת הורדה במקביל, מ×פשר הורדה ×חת בבת ×חת )"
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr "קלט שגוי! × × ×œ× ×¡×•×ª שוב ×¢× ×§×œ×˜ תקין [1 עד {max_downloads}, ×ו 0 להשבתה]"
+
+msgid "Parallel Downloads"
+msgstr "הורדות במקביל"
+
+msgid "ESC to skip"
+msgstr "‫ESC לדילוג"
+
+msgid "CTRL+C to reset"
+msgstr "CTRL+C ל×יפוס"
+
+msgid "TAB to select"
+msgstr "TAB לבחירה"
+
+msgid "[Default value: 0] > "
+msgstr "[ערך ברירת מחדל: 0] > "
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr "כדי להשתמש ×‘×ª×¨×’×•× ×”×–×”, × × ×œ×”×ª×§×™×Ÿ ×ת הגופן שתומך בשפה ×”×–×ת ידנית."
+
+msgid "The font should be stored as {}"
+msgstr "יש ל×חסן ×ת הגופן בתור {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "‫Archinstall דורש הרש×ות עליונות (root) כדי לעלות. ‎--help לקבלת מידע נוסף."
+
+msgid "Select an execution mode"
+msgstr "× × ×œ×‘×—×•×¨ מצב הפעלה"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "×œ× × ×™×ª×Ÿ למשוך ×ת הפרופיל מהכתובת שצוינה: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "השמות של ×”×¤×¨×•×¤×™×œ×™× ×—×™×™×‘×™× ×œ×”×™×•×ª יחודיי×, ×ך נמצ×ו הגדרות ×¤×¨×•×¤×™×œ×™× ×¢× ×©×ž×•×ª כפולי×: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "× × ×œ×‘×—×•×¨ התקן ×ו יותר לשימוש ולהגדרה"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "×יפוס בחירת ×”×”×ª×§× ×™× ×™×פס ×’× ×ת פריסת ×”×›×•× × ×™× ×”× ×•×›×—×™×ª. להמשיך?"
+
+msgid "Existing Partitions"
+msgstr "מחיצות קיימות"
+
+msgid "Select a partitioning option"
+msgstr "× × ×œ×‘×—×•×¨ ×פשרויות מחיצות"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "× × ×œ×ž×œ× ×ת תיקיית השורש של ×”×”×ª×§× ×™× ×”×ž×¢×•×’× ×™×: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "קיבולת מזערית למחיצת ‎/home: {} ג״ב\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "קיבולת מזערית למחיצת Arch Linux: {} ג״ב"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "×–×ת רשימה של פרופילי גיבוי שנכתבו מר×ש, ×”× ×¢×©×•×™×™× ×œ×”×§×œ על התקנת ×“×‘×¨×™× ×›×ž×• סביבות שולחן עבודה"
+
+msgid "Current profile selection"
+msgstr "בחירת הפרופיל הנוכחי"
+
+msgid "Remove all newly added partitions"
+msgstr "הסרת כל המחיצות החדשות שנוספו"
+
+msgid "Assign mountpoint"
+msgstr "הקצ×ת נקודת ×¢×’×™× ×”"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "סימון/ביטול סימן לפרמוט (מוחה ×ת הנתוני×)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "סימון/ביטול סימון כזמין לטעינה"
+
+msgid "Change filesystem"
+msgstr "החלפת מערכת קבצי×"
+
+msgid "Mark/Unmark as compressed"
+msgstr "סימון/ביטול סימון כמכווץ"
+
+msgid "Set subvolumes"
+msgstr "הגדרת תת־כרכי×"
+
+msgid "Delete partition"
+msgstr "מחיקת מחיצה"
+
+msgid "Partition"
+msgstr "מחיצה"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "המחיצה ×”×–×ת מוצפנת כרגע, כדי לפרמט ×ותה צריך להגדיר מערכת קבצי×"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "נקודות העגינה של המחיצה הן יחסיות למערכת ההתקנה, מחיצת הטעינה למשל תהיה ‎/boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "×× ×ž×•×’×“×¨×ª נקודת ×¢×’×™× ×” על ‎/boot, ××– המחיצה תסומן כזמינה לטעינה."
+
+msgid "Mountpoint: "
+msgstr "נקודת עגינה: "
+
+msgid "Current free sectors on device {}:"
+msgstr "כמות ×”×ž×§×˜×¢×™× (סקטורי×) ×”×¤× ×•×™×™× ×›×¨×’×¢ בכונן {}:"
+
+msgid "Total sectors: {}"
+msgstr "סך כל ×”×ž×§×˜×¢×™× (סקטורי×): {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "× × ×œ×ž×œ× ×ת מקטע (סקטור) ההתחלה (ברירת מחדל: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "× × ×œ×ž×œ× ×ת מקטע (סקטור) הסוף של המחיצה (×חוז ×ו מספר בלוק, ברירת מחדל: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "הפעולה ×”×–×ת תסיר ×ת המחיצות שנוספו, להמשיך?"
+
+msgid "Partition management: {}"
+msgstr "ניהול מחיצות: {}"
+
+msgid "Total length: {}"
+msgstr "×ורך כולל: {}"
+
+msgid "Encryption type"
+msgstr "סוג הצפנה"
+
+msgid "Partitions"
+msgstr "מחיצות"
+
+msgid "No HSM devices available"
+msgstr "×ין התקני HSM זמיני×"
+
+msgid "Partitions to be encrypted"
+msgstr "מחיצות להצפנה"
+
+msgid "Select disk encryption option"
+msgstr "× × ×œ×‘×—×•×¨ ×פשרויות להצפנת כונן"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "× × ×œ×‘×—×•×¨ ×ת התקן ×”Ö¾FIDO2 לשימוש עבור HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "להשתמש בפריסת מחיצות כברירת מחדל על בסיס מ×מץ מיטבי"
+
+msgid "Manual Partitioning"
+msgstr "חלוקה ידנית למחיצות"
+
+msgid "Pre-mounted configuration"
+msgstr "הגדרות לעיגון שבוצע"
+
+msgid "Unknown"
+msgstr "×œ× ×™×“×•×¢"
+
+msgid "Partition encryption"
+msgstr "הצפנת מחיצה"
+
+msgid " ! Formatting {} in "
+msgstr " ! {} מפורמט תחת "
+
+msgid "↠Back"
+msgstr "→ חזרה"
+
+msgid "Disk encryption"
+msgstr "הצפנת כונן"
+
+msgid "Configuration"
+msgstr "הגדרות"
+
+msgid "Password"
+msgstr "סיסמה"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "כל ההגדרות ת×ופסנה, להמשיך?"
+
+msgid "Back"
+msgstr "חזרה"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "× × ×œ×‘×—×•×¨ ×ת מערכת קבלת ×”×¤× ×™× ×œ×¤×¨×•×¤×™×œ×™× ×”× ×‘×—×¨×™×: {}"
+
+msgid "Environment type: {}"
+msgstr "סוג סביבה: {}"
+
+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 "ב־Sway ×ין תמיכה במנהל ההתקן של Nvidia. כנר××” שזה יוביל לבעיות, ×–×” בסדר מבחינתך?"
+
+msgid "Installed packages"
+msgstr "חבילות מותקנות"
+
+msgid "Add profile"
+msgstr "הוספת פרופיל"
+
+msgid "Edit profile"
+msgstr "עריכת פרופיל"
+
+msgid "Delete profile"
+msgstr "מחיקת פרופיל"
+
+msgid "Profile name: "
+msgstr "×©× ×”×¤×¨×•×¤×™×œ: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "×©× ×”×¤×¨×•×¤×™×œ שמיל×ת כבר קיי×. × × ×œ× ×¡×•×ª שוב"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "חבילות להתקנה ×¢× ×”×¤×¨×•×¤×™×œ ×”×–×” (להפריד ברווחי×, ריק יוביל לדילוג על השלב ×”×–×”): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "×©×™×¨×•×ª×™× ×œ×”×¤×¢×œ×” ×¢× ×”×¤×¨×•×¤×™×œ ×”×–×” (להפריד ברווחי×, ריק יוביל לדילוג על השלב ×”×–×”): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "להפעיל ×ת הפרופיל ×”×–×” להתקנה?"
+
+msgid "Create your own"
+msgstr "יצירת ×חד משלך"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"â€«× × ×œ×‘×—×•×¨ מנהל התקן גרפי ×ו להש×יר ריק כדי להתקין ×ת מנהל ההתקן שכולו בקוד פתוח"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "‫Sway צריך גישה למושב (×וסף של התקני חומרה כמו למשל מקלדת, עכבר וכו׳) שלך"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"â€«× × ×œ×‘×—×•×¨ ×פשרות לתת ל־Sway גישה לחומרה שלך"
+
+msgid "Graphics driver"
+msgstr "מנהלי התקני גרפיי×"
+
+msgid "Greeter"
+msgstr "מערכת קבלת פני×"
+
+msgid "Please chose which greeter to install"
+msgstr "× × ×œ×‘×—×•×¨ ×יזו מערכת קבלת ×¤× ×™× ×œ×”×ª×§×™×Ÿ"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "×–×ת רשימה של ×¤×¨×•×¤×™×œ×™× ×‘×¨×™×¨×ª מחדל שנכתבו מר×ש"
+
+msgid "Disk configuration"
+msgstr "הגדרת כונן"
+
+msgid "Profiles"
+msgstr "פרופילי×"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "מתבצע חיפוש ×חר תיקיות ×פשריות לשמירת קובצי ההגדרות…"
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "× × ×œ×‘×—×•×¨ תיקייה (×ו תיקיות) לשמירת קובצי ההגדרות"
+
+msgid "Add a custom mirror"
+msgstr "הוספת ×תר מר××” משלך"
+
+msgid "Change custom mirror"
+msgstr "החלפת ×תר מר××” משלך"
+
+msgid "Delete custom mirror"
+msgstr "מחיקת ×תר מר××” משלך"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "× × ×œ×ž×œ× ×©× (ריק לדילוג): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "× × ×œ×ž×œ× ×›×ª×•×‘×ª (ריק לדילוג): "
+
+msgid "Select signature check option"
+msgstr "בחירת ×פשרות בדיקת חתימות"
+
+msgid "Select signature option"
+msgstr "בחירת ×פשרות חתימות"
+
+msgid "Custom mirrors"
+msgstr "×תרי מר××” משלך"
+
+msgid "Defined"
+msgstr "מוגדר"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "שמירת הגדרות משתמש (כולל פריסת כונני×)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"× × ×œ×ž×œ× ×ª×™×§×™×™×” לשמירת ההגדרות (×פשר ×œ×”×©×œ×™× ×¢× tab)\n"
+"‬\n"
+"‫תיקיית השמירה: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"לשמור ×ת {} קובצי ההגדרות בתיקייה הב××”?‬\n"
+"\n"
+"‫{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "קובצי ההגדרות {} × ×©×ž×¨×™× ×ל {}"
+
+msgid "Mirrors"
+msgstr "×תרי מר××”"
+
+msgid "Mirror regions"
+msgstr "×זורי ×תרי מר××”"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - ערך מרבי : {} ( מ×פשר {} הורדות במקביל, מ×פשר {max_downloads+1} הורדות במקביל )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "קלט שגוי! × × ×œ× ×¡×•×ª שוב ×¢× ×§×œ×˜ תקין [1 עד {}, ×ו 0 להשבתה]"
+
+msgid "Locales"
+msgstr "הגדרות ×זוריות"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "להשתמש ב־NetworkManager (נחוץ להגדרת ×”×ינטרנט ב×ופן גרפי ב־GNOME וב־KDE)"
+
+msgid "Total: {} / {}"
+msgstr "סך הכול: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "×פשר להוסיף יחידה לכל ערך ×©×”×•× ×©×”×–× ×ª: %, B,†KB,†KiB,†MB,†MiB…"
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "×× ×œ× ×¦×•×™×Ÿ ערך, הערך יפורש בתוך ×¡×§×˜×•×¨×™× (מגזרי×)"
+
+msgid "Enter start (default: sector {}): "
+msgstr "× × ×œ×ž×œ× ×”×ª×—×œ×” (ברירת מחדל: סקטור {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "× × ×œ×ž×œ× ×¡×•×£ (ברירת מחדל: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "×œ× × ×™×ª×Ÿ ×œ×ž×¦×•× ×”×ª×§× ×™ fido2.†libfido2 מותקן?"
+
+msgid "Path"
+msgstr "נתיב"
+
+msgid "Manufacturer"
+msgstr "יצרן"
+
+msgid "Product"
+msgstr "מוצר"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "הגדרה שגויה: {error}"
+
+msgid "Type"
+msgstr "סוג"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "×פשרות זו מ×פשרת כמה הורדות במקביל לטובת הורדות של חבילות"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"× × ×œ×ž×œ× ×ת מספר ההורדות המקביליות להפעלה.‬\n"
+"\n"
+"‫הערה:‬\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - הערך המרבי המומלץ: {} ( מ×פשר {} הורדות במקביל בכל עת )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - השבתה/ברירת מחדל : 0 ( משבית הורדות מקביליות, מ×פשר רק הורדה ×חת כל ×¤×¢× )‬\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "קלט שגוי! × × ×œ× ×¡×•×ª שוב ×¢× ×§×œ×˜ תקין [×ו 0 להשבתה]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "‫Hyprland צריך גישה למושב שלך (seat - ×וסף של התקני חומרה, כלומר מקלדת, עכבר וכו׳)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"â€«× × ×œ×‘×—×•×¨ ×פשרות כדי לתת ל־Hyprland גישה לחומרה שלך"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "×פשר להוסיף יחידה לכל ערך שהו×: %, B,†KB,†KiB,†MB,†MiB…"
+
+msgid "Would you like to use unified kernel images?"
+msgstr "להשתמש בדמויות ליבה ×חודות (UKI)?"
+
+msgid "Unified kernel images"
+msgstr "דמויות ליבה ×חודות"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "בהמתנה להשלמת סנכרון השעון (timedatectl show)."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "סנכרון זמן ×œ× ×ž×¡×ª×™×™×, בזמן ההמתנה - כד××™ לחפש צורות לעקוף ×ת ×–×” במסמכי×: https://archinstall.readthedocs.io/‎"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "לדלג על המתנה לסנכרון השעון ×וטומטית (יכול ×œ×’×¨×•× ×œ×‘×¢×™×•×ª ×× ×”×©×¢×” ×œ× ×ž×¡×•× ×›×¨× ×ª במהלך ההתקנה)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "בהמתנה ×œ×¡×™×•× ×¡× ×›×¨×•×Ÿ מחזיק מפתחות של Arch Linux†(archlinux-keyring-wkd-sync)."
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "מחיקת פרופיל"
+
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "סנכרון זמן ×œ× ×ž×¡×ª×™×™×, בזמן ההמתנה - כד××™ לחפש צורות לעקוף ×ת ×–×” במסמכי×: https://archinstall.readthedocs.io/‎"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "סימון/ביטול סימון כזמין לטעינה"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "להשתמש בדחיסה של BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/hi/LC_MESSAGES/base.mo b/archinstall/locales/hi/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..65ce321c
--- /dev/null
+++ b/archinstall/locales/hi/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/hi/LC_MESSAGES/base.po b/archinstall/locales/hi/LC_MESSAGES/base.po
new file mode 100644
index 00000000..c1ad7eae
--- /dev/null
+++ b/archinstall/locales/hi/LC_MESSAGES/base.po
@@ -0,0 +1,1181 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: hi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.2.2\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr ""
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr ""
+
+msgid "Do you really want to abort?"
+msgstr ""
+
+msgid "And one more time for verification: "
+msgstr "सतà¥à¤¯à¤¾à¤ªà¤¨ के लिठà¤à¤• बार और: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "कà¥à¤¯à¤¾ आप swap के लिठzram इसà¥à¤¤à¥‡à¤®à¤¾à¤² करना चाहेंगे?"
+
+msgid "Desired hostname for the installation: "
+msgstr "इचà¥à¤›à¤¿à¤¤ होसà¥à¤Ÿ का नाम (hostname) इंसà¥à¤Ÿà¤¾à¤²à¥‡à¤¶à¤¨ के लिà¤: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr ""
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr ""
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "कà¥à¤¯à¤¾ यह user à¤à¤• superuser होना चाहिठ(sudoer)?"
+
+msgid "Select a timezone"
+msgstr "à¤à¤• समयकà¥à¤·à¥‡à¤¤à¥à¤° चà¥à¤¨à¥‡à¤‚"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "कà¥à¤¯à¤¾ आप systemd-boot की जगह GRUB इसà¥à¤¤à¥‡à¤®à¤¾à¤² करना चाहेंगे bootloader ke liye?"
+
+msgid "Choose a bootloader"
+msgstr "à¤à¤• bootloader चà¥à¤¨à¥‡à¤‚"
+
+msgid "Choose an audio server"
+msgstr "à¤à¤• audio server चà¥à¤¨à¥‡à¤‚"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr ""
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr ""
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "अनà¥à¤¯ packages का नाम लिखें (सà¥à¤ªà¥‡à¤¸ छोड़कर, अनà¥à¤¯à¤¥à¤¾ खाली छोड़ें): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr ""
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr ""
+
+msgid "Select one network interface to configure"
+msgstr ""
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr ""
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr ""
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr ""
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr ""
+
+msgid "Select which filesystem your main partition should use"
+msgstr ""
+
+msgid "Current partition layout"
+msgstr ""
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr ""
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr ""
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr ""
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr ""
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr ""
+
+msgid "Archinstall language"
+msgstr ""
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr ""
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr ""
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr ""
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr ""
+
+msgid "Select keyboard layout"
+msgstr ""
+
+msgid "Select one of the regions to download packages from"
+msgstr ""
+
+msgid "Select one or more hard drives to use and configure"
+msgstr ""
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr ""
+
+msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
+msgstr ""
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid "All open-source (default)"
+msgstr ""
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr ""
+
+msgid "Choose which locale language to use"
+msgstr ""
+
+msgid "Choose which locale encoding to use"
+msgstr ""
+
+msgid "Select one of the values shown below: "
+msgstr ""
+
+msgid "Select one or more of the options below: "
+msgstr ""
+
+msgid "Adding partition...."
+msgstr ""
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr ""
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr ""
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr ""
+
+msgid "Keyboard layout"
+msgstr ""
+
+msgid "Mirror region"
+msgstr ""
+
+msgid "Locale language"
+msgstr ""
+
+msgid "Locale encoding"
+msgstr ""
+
+msgid "Drive(s)"
+msgstr ""
+
+msgid "Disk layout"
+msgstr ""
+
+msgid "Encryption password"
+msgstr ""
+
+msgid "Swap"
+msgstr ""
+
+msgid "Bootloader"
+msgstr ""
+
+msgid "Root password"
+msgstr ""
+
+msgid "Superuser account"
+msgstr ""
+
+msgid "User account"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Audio"
+msgstr ""
+
+msgid "Kernels"
+msgstr ""
+
+msgid "Additional packages"
+msgstr ""
+
+msgid "Network configuration"
+msgstr ""
+
+msgid "Automatic time sync (NTP)"
+msgstr ""
+
+msgid "Install ({} config(s) missing)"
+msgstr ""
+
+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 ""
+
+msgid "Re-using partition instance: {}"
+msgstr ""
+
+msgid "Create a new partition"
+msgstr ""
+
+msgid "Delete a partition"
+msgstr ""
+
+msgid "Clear/Delete all partitions"
+msgstr ""
+
+msgid "Assign mount-point for a partition"
+msgstr ""
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr ""
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr ""
+
+msgid "Set desired filesystem for a partition"
+msgstr ""
+
+msgid "Abort"
+msgstr ""
+
+msgid "Hostname"
+msgstr ""
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr ""
+
+msgid "Timezone"
+msgstr ""
+
+msgid "Set/Modify the below options"
+msgstr ""
+
+msgid "Install"
+msgstr ""
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+
+msgid "Suggest partition layout"
+msgstr ""
+
+msgid "Enter a password: "
+msgstr ""
+
+msgid "Enter a encryption password for {}"
+msgstr ""
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr ""
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr ""
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr ""
+
+msgid "Password for user \"{}\": "
+msgstr ""
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr ""
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr ""
+
+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 ""
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr ""
+
+msgid "Use ESC to skip\n"
+msgstr ""
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Confirm and exit"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Copy"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Select an action for '{}'"
+msgstr ""
+
+msgid "Copy to new key:"
+msgstr ""
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr ""
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr ""
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr ""
+
+msgid "Choose which optional additional repositories to enable"
+msgstr ""
+
+msgid "Add a user"
+msgstr ""
+
+msgid "Change password"
+msgstr ""
+
+msgid "Promote/Demote user"
+msgstr ""
+
+msgid "Delete User"
+msgstr ""
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+
+msgid "User Name : "
+msgstr ""
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr ""
+
+msgid "Define users with sudo privilege: "
+msgstr ""
+
+msgid "No network configuration"
+msgstr ""
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr ""
+
+msgid "No configuration"
+msgstr ""
+
+msgid "Save user configuration"
+msgstr ""
+
+msgid "Save user credentials"
+msgstr ""
+
+msgid "Save disk layout"
+msgstr ""
+
+msgid "Save all"
+msgstr ""
+
+msgid "Choose which configuration to save"
+msgstr ""
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr ""
+
+msgid "Not a valid directory: {}"
+msgstr ""
+
+msgid "The password you are using seems to be weak,"
+msgstr ""
+
+msgid "are you sure you want to use it?"
+msgstr ""
+
+msgid "Optional repositories"
+msgstr ""
+
+msgid "Save configuration"
+msgstr ""
+
+msgid "Missing configurations:\n"
+msgstr ""
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr ""
+
+msgid "Manage superuser accounts: "
+msgstr ""
+
+msgid "Manage ordinary user accounts: "
+msgstr ""
+
+msgid " Subvolume :{:16}"
+msgstr ""
+
+msgid " mounted at {:16}"
+msgstr ""
+
+msgid " with option {}"
+msgstr ""
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+
+msgid "Subvolume name "
+msgstr ""
+
+msgid "Subvolume mountpoint"
+msgstr ""
+
+msgid "Subvolume options"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Subvolume name :"
+msgstr ""
+
+msgid "Select a mount point :"
+msgstr ""
+
+msgid "Select the desired subvolume options "
+msgstr ""
+
+msgid "Define users with sudo privilege, by username: "
+msgstr ""
+
+msgid "[!] A log file has been created here: {}"
+msgstr ""
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr ""
+
+msgid "Would you like to use BTRFS compression?"
+msgstr ""
+
+msgid "Would you like to create a separate partition for /home?"
+msgstr ""
+
+msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "yes"
+msgstr ""
+
+msgid "no"
+msgstr ""
+
+msgid "set: {}"
+msgstr ""
+
+msgid "Manual configuration setting must be a list"
+msgstr ""
+
+msgid "No iface specified for manual configuration"
+msgstr ""
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr ""
+
+msgid "Add interface"
+msgstr ""
+
+msgid "Edit interface"
+msgstr ""
+
+msgid "Delete interface"
+msgstr ""
+
+msgid "Select interface to add"
+msgstr ""
+
+msgid "Manual configuration"
+msgstr ""
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr ""
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr ""
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr ""
+
+msgid "Select your desired desktop environment"
+msgstr ""
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr ""
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr ""
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr ""
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr ""
+
+msgid "Press Enter to continue."
+msgstr ""
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr ""
+
+msgid "Are you sure you want to reset this setting?"
+msgstr ""
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr ""
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr ""
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+msgid "Save and exit"
+msgstr "सहेजें और छोड़ें"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+
+msgid "No audio server"
+msgstr ""
+
+msgid "(default)"
+msgstr ""
+
+msgid "Use ESC to skip"
+msgstr "छोड़ने के लिठESC का उपयोग करें"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+
+msgid "Copy to: "
+msgstr ""
+
+msgid "Edit: "
+msgstr ""
+
+msgid "Key: "
+msgstr ""
+
+msgid "Edit {}: "
+msgstr ""
+
+msgid "Add: "
+msgstr ""
+
+msgid "Value: "
+msgstr ""
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr ""
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr ""
+
+msgid "Select which partitions to mark for formatting:"
+msgstr ""
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr ""
+
+msgid "Device"
+msgstr ""
+
+msgid "Size"
+msgstr ""
+
+msgid "Free space"
+msgstr "खाली जगह"
+
+msgid "Bus-type"
+msgstr ""
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr ""
+
+msgid "Enter username (leave blank to skip): "
+msgstr ""
+
+msgid "The username you entered is invalid. Try again"
+msgstr ""
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr ""
+
+msgid "Select which partitions to encrypt"
+msgstr ""
+
+msgid "very weak"
+msgstr "बहà¥à¤¤ कमजोर"
+
+msgid "weak"
+msgstr "कमजोर"
+
+msgid "moderate"
+msgstr "मधà¥à¤¯à¤®"
+
+msgid "strong"
+msgstr "मज़बूत"
+
+msgid "Add subvolume"
+msgstr ""
+
+msgid "Edit subvolume"
+msgstr ""
+
+msgid "Delete subvolume"
+msgstr ""
+
+msgid "Configured {} interfaces"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr ""
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr ""
+
+msgid "Parallel Downloads"
+msgstr ""
+
+msgid "ESC to skip"
+msgstr ""
+
+msgid "CTRL+C to reset"
+msgstr ""
+
+msgid "TAB to select"
+msgstr ""
+
+msgid "[Default value: 0] > "
+msgstr ""
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr ""
+
+msgid "The font should be stored as {}"
+msgstr ""
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+msgid "Select an execution mode"
+msgstr ""
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+msgid "Select one or more devices to use and configure"
+msgstr ""
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+msgid "Existing Partitions"
+msgstr ""
+
+msgid "Select a partitioning option"
+msgstr ""
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr ""
+
+msgid "Current profile selection"
+msgstr ""
+
+msgid "Remove all newly added partitions"
+msgstr ""
+
+msgid "Assign mountpoint"
+msgstr ""
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+msgid "Mark/Unmark as compressed"
+msgstr ""
+
+msgid "Set subvolumes"
+msgstr ""
+
+msgid "Delete partition"
+msgstr ""
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr ""
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+msgid "Total sectors: {}"
+msgstr ""
+
+msgid "Enter the start sector (default: {}): "
+msgstr ""
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr ""
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
+msgid "Encryption type"
+msgstr ""
+
+msgid "Partitions"
+msgstr ""
+
+msgid "No HSM devices available"
+msgstr ""
+
+msgid "Partitions to be encrypted"
+msgstr ""
+
+msgid "Select disk encryption option"
+msgstr ""
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr ""
+
+msgid "Use a best-effort default partition layout"
+msgstr ""
+
+msgid "Manual Partitioning"
+msgstr ""
+
+msgid "Pre-mounted configuration"
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
+msgstr ""
+
+msgid "Disk encryption"
+msgstr ""
+
+msgid "Configuration"
+msgstr ""
+
+msgid "Password"
+msgstr "पासवरà¥à¤¡"
+
+msgid "All settings will be reset, are you sure?"
+msgstr ""
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+msgid "Installed packages"
+msgstr ""
+
+msgid "Add profile"
+msgstr ""
+
+msgid "Edit profile"
+msgstr ""
+
+msgid "Delete profile"
+msgstr ""
+
+msgid "Profile name: "
+msgstr ""
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+msgid "Disk configuration"
+msgstr ""
+
+msgid "Profiles"
+msgstr ""
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr ""
+
+msgid "Add a custom mirror"
+msgstr ""
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+msgid "Enter name (leave blank to skip): "
+msgstr ""
+
+msgid "Enter url (leave blank to skip): "
+msgstr ""
+
+msgid "Select signature check option"
+msgstr ""
+
+msgid "Select signature option"
+msgstr ""
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+msgid "Save user configuration (including disk layout)"
+msgstr ""
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+msgid "Saving {} configuration files to {}"
+msgstr ""
+
+msgid "Mirrors"
+msgstr ""
+
+msgid "Mirror regions"
+msgstr ""
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr ""
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr ""
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+msgid "Enter start (default: sector {}): "
+msgstr ""
+
+msgid "Enter end (default: {}): "
+msgstr ""
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "Would you like to use unified kernel images?"
+msgstr ""
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+msgid "Selected profiles: "
+msgstr ""
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Mark/Unmark as nodatacow"
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "कà¥à¤¯à¤¾ आप swap के लिठzram इसà¥à¤¤à¥‡à¤®à¤¾à¤² करना चाहेंगे?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/hu/LC_MESSAGES/base.mo b/archinstall/locales/hu/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..0f1c4762
--- /dev/null
+++ b/archinstall/locales/hu/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/hu/LC_MESSAGES/base.po b/archinstall/locales/hu/LC_MESSAGES/base.po
new file mode 100644
index 00000000..52f675dc
--- /dev/null
+++ b/archinstall/locales/hu/LC_MESSAGES/base.po
@@ -0,0 +1,1247 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: summoner <summoner@vivaldi.net>\n"
+"Language-Team: \n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.4.2\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] A naplófájl itt jött létre: {} {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " Jelentse ezt a problémát (a naplófájllal együtt) itt: https://github.com/archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "Valóban megszakítja?"
+
+msgid "And one more time for verification: "
+msgstr "És még egyszer az ellenőrzéshez: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "Biztosan cserehelyet akar használni a zram-on?"
+
+msgid "Desired hostname for the installation: "
+msgstr "A számítógép neve a telepítéshez: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "A rendszergazda felhasználó neve a számítógép használatához szükséges sudo jogosultságokkal: "
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "További hozzáadandó felhasználók (ha nem akar több felhasználót hozzáadni, akkor hagyja üresen): "
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "Ennek a felhasználónak rendszergazdának (sudoer-nek) kell lennie?"
+
+msgid "Select a timezone"
+msgstr "Időzóna kiválasztása"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "Szeretné használni a GRUB-ot rendszerbetöltőként a systemd-boot helyett?"
+
+msgid "Choose a bootloader"
+msgstr "Válasszon ki egy rendszerbetöltőt"
+
+msgid "Choose an audio server"
+msgstr "Válasszon ki egy hangkiszolgálót"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "Csak az olyan csomagok mint a base, base-devel, linux, linux-firmware, efibootmgr és az opcionális profilcsomagok kerülnek telepítésre."
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr "Ha olyan böngészőre vágyik, mint például a Firefox vagy a Chromium, akkor azt a következő prompt-ban adhatja meg."
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "Ãrjon be további telepítendÅ‘ csomag neveket (szóközzel elválasztva, ha ki akarja hagyni ezt lépést, akkor üresen hagyható): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr "Másolja be az ISO hálózati konfigurációt a telepítéshez"
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "A hálózatkezelő használata (szükséges az internet grafikus konfigurálásához GNOME-ban és KDE-ben)"
+
+msgid "Select one network interface to configure"
+msgstr "Válasszon ki egy hálózati interfészt a konfiguráláshoz"
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr "Válassza ki, hogy melyik módot szeretné beállítani a(z) \"{}\" számára, vagy hagyja ki ezt a lépést az alapértelmezett \"{}\" mód használatához"
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "Adja meg az IP-címét és alhálózatát (például: 192.168.0.5/24) ehhez: {}: "
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "Adja meg az átjáró (router) IP-címét, vagy hagyja üresen, ha nincs ilyen: "
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr "Adja meg DNS-kiszolgálókat (szóközzel elválasztva, vagy hagyja üresen, ha nincsenek ilyenek): "
+
+msgid "Select which filesystem your main partition should use"
+msgstr "Válassza ki, hogy a fő partíció milyen fájlrendszert használjon"
+
+msgid "Current partition layout"
+msgstr "A partíció jelenlegi elrendezése"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"Válassza ki, hogy mi legyen a teendő a következővel:\n"
+"{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "Adja meg a partíció kívánt fájlrendszer típusát"
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "Adja meg a kiindulási helyet (felosztott egységekben: s, GB, % stb. ; alapértelmezett: {}): "
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "Adja meg a befejezési helyet (felosztott egységekben: s, GB, % stb. ; pl.: {}): "
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "A(z) {} sorban álló partíciókat tartalmaz, ez eltávolítja azokat, biztos benne?"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}\n"
+"\n"
+"Index alapján válassza ki a törölni kívánt partíciókat"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}\n"
+"\n"
+"Index alapján válassza ki, hogy melyik partíciót hová szeretné csatolni"
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * A partíció csatolási pontjai a telepítésen belülre vonatkoznak, a boot pédául /boot lesz."
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr "Válassza ki, hogy hová szeretné csatolni a partíciót (hagyja üresen a csatolási pont eltávolításához): "
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"{}\n"
+"\n"
+"Válassza ki, hogy melyik partíciót szeretné formázásra megjelölni"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"{}\n"
+"\n"
+"Válassza ki, hogy melyik partíciót szeretné titkosítottként megjelölni"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"{}\n"
+"\n"
+"Válassza ki, hogy melyik partíciót szeretné rendszerbetöltőként megjelölni"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"{}\n"
+"\n"
+"Válassza ki, hogy melyik partícióra kívánja a fájlrendszert beállítani"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "Adja meg a kívánt fájlrendszer típusát a partíciónak: "
+
+msgid "Archinstall language"
+msgstr "Az Archtelepítő nyelve"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr "Törölje az összes kiválasztott meghajtót, és használja a lehető legjobb beállítást lehetővé tévő alapértelmezett partíció-elrendezést"
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr "Válassza ki, hogy mit tegyen a telepítő az egyes meghajtókkal (ezt a partícióhasználat követi)"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "Válassza ki, hogy mit tegyen a telepítő a kiválasztott blokk-eszközökkel"
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr "Ez az előre programozott profilok listája, amelyek megkönnyíthetik az olyan dolgok telepítését, mint például az asztali környezetek"
+
+msgid "Select keyboard layout"
+msgstr "Válassza ki a billentyűzetkiosztást"
+
+msgid "Select one of the regions to download packages from"
+msgstr "Válasszon ki egy régiót a csomagok letöltéséhez"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "Válasszon ki egy vagy több meghajtót a használathoz és a konfiguráláshoz"
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr "Az AMD hardverrel való legjobb kompatibilitás érdekében érdemes lehet az összes nyílt forráskódú vagy az AMD / ATI opciót használni."
+
+msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
+msgstr "Az Intel hardverrel való legjobb kompatibilitás érdekében érdemes lehet az összes nyílt forráskódú vagy Intel opciót használni.\n"
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr "Az Nvidia hardverrel való legjobb kompatibilitás érdekében érdemes lehet az Nvidia saját fejlesztésű illesztőprogramját használni.\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Válasszon ki egy grafikus illesztőprogramot, vagy hagyja üresen az összes nyílt forráskódú illesztőprogram telepítéséhez"
+
+msgid "All open-source (default)"
+msgstr "Minden nyílt forráskódú (alapértelmezett)"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "Válassza ki a használni kívánt kerneleket, vagy hagyja üresen az alapértelmezetthez \"{}\""
+
+msgid "Choose which locale language to use"
+msgstr "Válassza ki a használni kívánt területi nyelvet"
+
+msgid "Choose which locale encoding to use"
+msgstr "Válassza ki a használni kívánt nyelvi kódolást"
+
+msgid "Select one of the values shown below: "
+msgstr "Válasszon az alábbi értékek egyikéből: "
+
+msgid "Select one or more of the options below: "
+msgstr "Válasszon ki egyet vagy többet az alábbi lehetőségek közül: "
+
+msgid "Adding partition...."
+msgstr "Partíció hozzáadása...."
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr "A folytatáshoz meg kell adnia egy érvényes fs-típust. Az érvényes fs-típusokat megtekinthetőek a `man parted` részben."
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "Hiba: A(z) \"{}\" URL-címen lévő profilok listázása a következőket eredményezte:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "Hiba: Nem sikerült dekódolni a(z) \"{}\" eredményt JSON-ként:"
+
+msgid "Keyboard layout"
+msgstr "Billentyűzetkiosztás"
+
+msgid "Mirror region"
+msgstr "Tükör régió"
+
+msgid "Locale language"
+msgstr "Helyi nyelv"
+
+msgid "Locale encoding"
+msgstr "Helyi kódolás"
+
+msgid "Drive(s)"
+msgstr "Meghajtó(k)"
+
+msgid "Disk layout"
+msgstr "Lemez elrendezése"
+
+msgid "Encryption password"
+msgstr "Titkosítási jelszó"
+
+msgid "Swap"
+msgstr "Cserehely"
+
+msgid "Bootloader"
+msgstr "Rendszerbetöltő"
+
+msgid "Root password"
+msgstr "Root jelszó"
+
+msgid "Superuser account"
+msgstr "Rendszergazda fiók"
+
+msgid "User account"
+msgstr "Felhasználói fiók"
+
+msgid "Profile"
+msgstr "Profil"
+
+msgid "Audio"
+msgstr "Hang"
+
+msgid "Kernels"
+msgstr "Kernelek"
+
+msgid "Additional packages"
+msgstr "További csomagok"
+
+msgid "Network configuration"
+msgstr "Hálózati konfiguráció"
+
+msgid "Automatic time sync (NTP)"
+msgstr "Automatikus időszinkronizálás (NTP)"
+
+msgid "Install ({} config(s) missing)"
+msgstr "Telepítés ({} konfiguráció hiányzik)"
+
+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 ""
+"Úgy döntött, hogy kihagyja a merevlemez kiválasztását\n"
+"és azt a meghajtóbeállítást fogja használni, amely a(z) {} helyen van csatolva (kísérleti)\n"
+"FIGYELMEZTETÉS: Az Archtelepítő nem ellenőrzi ennek a beállításnak a megfelelőségét\n"
+"Biztosan folytatni akarja?"
+
+msgid "Re-using partition instance: {}"
+msgstr "Partíciópéldány ismételt felhasználása: {}"
+
+msgid "Create a new partition"
+msgstr "Új partíció létrehozása"
+
+msgid "Delete a partition"
+msgstr "Partíció törlése"
+
+msgid "Clear/Delete all partitions"
+msgstr "Minden partíció tisztítása/törlése"
+
+msgid "Assign mount-point for a partition"
+msgstr "Csatolási pont hozzárendelése egy partícióhoz"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "Egy partíció megjelölése/megjelölés-visszavonása formázandóként (adatok törlése)"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "Egy partíció megjelölése/megjelölés-visszavonása titkosítottként"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "Egy partíció megjelölése/megjelölés-visszavonása mint rendszerindító (automatikus a /boot esetében)"
+
+msgid "Set desired filesystem for a partition"
+msgstr "Ãllítsa be a kívánt fájlrendszer típusát a partíciónak"
+
+msgid "Abort"
+msgstr "Megszakítás"
+
+msgid "Hostname"
+msgstr "A számítógép neve"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "Nincs konfigurálva, nem érhető el, kivéve, ha manuálisan állítja be"
+
+msgid "Timezone"
+msgstr "Időzóna"
+
+msgid "Set/Modify the below options"
+msgstr "Az alábbi opciók beállítása/módosítása"
+
+msgid "Install"
+msgstr "Telepítés"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"Használd az ESC billentyűt a kihagyáshoz\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "Partíció elrendezési javaslat"
+
+msgid "Enter a password: "
+msgstr "Adjon meg egy jelszót: "
+
+msgid "Enter a encryption password for {}"
+msgstr "Adjon meg egy titkosítási jelszót ehhez: {}"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "Adjon meg egy jelszót a lemez titkosításához (ha üresen hagyja nem lesz titkosítva): "
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "Hozzon létre egy szükséges rendszergazdát sudo jogosultságokkal: "
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "Adjon meg egy root jelszót (a root letiltásához hagyja üresen): "
+
+msgid "Password for user \"{}\": "
+msgstr "\"{}\" felhasználó jelszava: "
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr "További csomagok létezésének ellenőrzése (ez eltarthat néhány másodpercig)"
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr "Szeretné használni az automatikus időszinkronizálást (NTP) az alapértelmezett időszerverekkel?\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 ""
+"Az NTP működéséhez szükség lehet a hardveridőre és egyéb utólagos konfigurációs lépésekre.\n"
+"További információkért tekintse meg az Arch wiki-t"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "Adjon meg egy felhasználónevet egy további felhasználó létrehozásához (üresen hagyva ez a lépés kihagyható): "
+
+msgid "Use ESC to skip\n"
+msgstr ""
+"Használja az ESC billentyűt a kihagyáshoz\n"
+"\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+"\n"
+"Tallózzon ki egy objektumot a listából, majd válasszon ki egyet a végrehajtandó műveletek közül"
+
+msgid "Cancel"
+msgstr "Mégse"
+
+msgid "Confirm and exit"
+msgstr "Megerősítés és kilépés"
+
+msgid "Add"
+msgstr "Hozzáadás"
+
+msgid "Copy"
+msgstr "Másolás"
+
+msgid "Edit"
+msgstr "Szerkesztés"
+
+msgid "Delete"
+msgstr "Törlés"
+
+msgid "Select an action for '{}'"
+msgstr "Válasszon egy műveletet a következőhöz: '{}'"
+
+msgid "Copy to new key:"
+msgstr "Másolás új kulcsba:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "Ismeretlen nic típus: {}. Lehetséges értékek: {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"Ez a választott konfiguráció:"
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "A Pacman már fut, várjon maximum 10 percet a megszakításával."
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr "A már meglévő pacman zár soha nem lép ki. Az Archtelepítő használata előtt tisztítsa meg a meglévő pacman munkameneteket."
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "Válassza ki, hogy mely további opcionális tárolókat kívánja engedélyezni"
+
+msgid "Add a user"
+msgstr "Felhasználó hozzáadása"
+
+msgid "Change password"
+msgstr "Jelszó megváltoztatása"
+
+msgid "Promote/Demote user"
+msgstr "Felhasználó előléptetése/lefokozása"
+
+msgid "Delete User"
+msgstr "Felhasználó törlése"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"Új felhasználó definiálása\n"
+
+msgid "User Name : "
+msgstr "Felhasználónév : "
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "{} rendszergazda (sudoer) legyen?"
+
+msgid "Define users with sudo privilege: "
+msgstr "A sudo jogosultsággal rendelkező felhasználók meghatározása: "
+
+msgid "No network configuration"
+msgstr "Nincs hálózati konfiguráció"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "Ãllítsa be a kívánt alköteteket egy BTRFS partíción"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}\n"
+"\n"
+"Válassza ki, hogy melyik partícióra kívánja beállítani az alköteteket"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "Az aktuális partíció BTRFS alköteteinek kezelése"
+
+msgid "No configuration"
+msgstr "Nincs konfiguráció"
+
+msgid "Save user configuration"
+msgstr "Felhasználói konfiguráció mentése"
+
+msgid "Save user credentials"
+msgstr "Felhasználói hitelesítő adatok mentése"
+
+msgid "Save disk layout"
+msgstr "Lemez elrendezésének mentése"
+
+msgid "Save all"
+msgstr "Összes mentése"
+
+msgid "Choose which configuration to save"
+msgstr "Válassza ki a menteni kívánt konfigurációt"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "Adjon meg egy könyvtárat a menteni kívánt konfiguráció(k) számára: "
+
+msgid "Not a valid directory: {}"
+msgstr "Érvénytelen könyvtár: {}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "Úgy tűnik, hogy a megadott jelszó gyenge,"
+
+msgid "are you sure you want to use it?"
+msgstr "biztos, hogy használni akarja?"
+
+msgid "Optional repositories"
+msgstr "Opcionális tárolók"
+
+msgid "Save configuration"
+msgstr "Konfiguráció mentése"
+
+msgid "Missing configurations:\n"
+msgstr "Hiányzó konfigurációk:\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "Meg kell adni egy root-jelszót vagy legalább 1 rendszergazdát"
+
+msgid "Manage superuser accounts: "
+msgstr "Rendszergazdai fiókok kezelése: "
+
+msgid "Manage ordinary user accounts: "
+msgstr "Normál felhasználói fiókok kezelése: "
+
+msgid " Subvolume :{:16}"
+msgstr " Alkötet :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " csatolva ehhez {:16}"
+
+msgid " with option {}"
+msgstr " {} opcióval"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+"Töltse ki a kívánt értékeket egy új alkötethez \n"
+
+msgid "Subvolume name "
+msgstr "Alkötet neve "
+
+msgid "Subvolume mountpoint"
+msgstr "Alkötet csatolási pontja"
+
+msgid "Subvolume options"
+msgstr "Alkötet beállítások"
+
+msgid "Save"
+msgstr "Mentés"
+
+msgid "Subvolume name :"
+msgstr "Alkötet neve :"
+
+msgid "Select a mount point :"
+msgstr "Válasszon ki egy csatolási pontot :"
+
+msgid "Select the desired subvolume options "
+msgstr "Válassza ki a kívánt alkötet beállításokat "
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "Sudo jogosultsággal rendelkező felhasználók meghatározása felhasználónév alapján: "
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] A naplófájl itt jött létre: {}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "Szeretne BTRFS alköteteket alapértelmezett struktúrával használni?"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "Szeretne BTRFS tömörítést használni?"
+
+msgid "Would you like to create a separate partition for /home?"
+msgstr "Szeretne egy külön partíciót létrehozni a /home számára?"
+
+msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
+msgstr "A kiválasztott meghajtók nem rendelkeznek az automatikus javaslathoz szükséges minimális kapacitással.\n"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "A /home partíció minimális kapacitása: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "Az Arch Linux partíció minimális kapacitása: {}GB"
+
+msgid "Continue"
+msgstr "Folytatás"
+
+msgid "yes"
+msgstr "igen"
+
+msgid "no"
+msgstr "nem"
+
+msgid "set: {}"
+msgstr "beállít: {}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "A kézi konfigurációs beállításnak egy listának kell lennie"
+
+msgid "No iface specified for manual configuration"
+msgstr "Nincs megadva iface a kézi konfigurációhoz"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr "A kézi nic konfiguráció automatikus DHCP nélkül egy IP-címet igényel"
+
+msgid "Add interface"
+msgstr "Interfész hozzáadása"
+
+msgid "Edit interface"
+msgstr "Interfész szerkesztése"
+
+msgid "Delete interface"
+msgstr "Interfész törlése"
+
+msgid "Select interface to add"
+msgstr "Válassza ki a hozzáadandó interfészt"
+
+msgid "Manual configuration"
+msgstr "Kézi konfiguráció"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "A partíció tömörítettként való megjelölése/megjelölés-visszavonása (csak btrfs)"
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "Úgy tűnik, hogy a megadott jelszó gyenge. Biztos, hogy használni akarja?"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr "Asztali környezetek és ablakkezelők széles választékát kínálja, pl. gnome, kde, sway"
+
+msgid "Select your desired desktop environment"
+msgstr "Válassza ki a kívánt asztali környezetet"
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr "Egy nagyon alapszintű telepítés, amely lehetővé teszi, hogy az Arch Linuxot saját belátása szerint testre szabja."
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr "Különböző szervercsomagok széles választékát kínálja a telepítéshez és az engedélyezéshez, pl. httpd, nginx, mariadb"
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr "Válassza ki, hogy mely szervereket kívánja telepíteni, ha egyiket sem, akkor minimális telepítés történik"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr "Telepít egy minimális rendszert, valamint a xorg és a grafikus illesztőprogramokat."
+
+msgid "Press Enter to continue."
+msgstr "A folytatáshoz nyomja meg az Entert."
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr "Szeretne chroot-olni az újonnan létrehozott telepítésbe, és elvégezni a telepítés utáni konfigurációt?"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "Biztos, hogy vissza akarja állítani ezt a beállítást alaphelyzetbe?"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "Válasszon ki egy vagy több meghajtót a használathoz és konfiguráláshoz.\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "A meglévő beállítások bármilyen módosítása visszaállítja a lemez elrendezését az alaphelyzetbe!"
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr "Ha alaphelyzetbe állítja a meghajtó-kiválasztást, akkor az aktuális lemezelrendezést is visszaállítja. Biztos benne?"
+
+msgid "Save and exit"
+msgstr "Mentés és kilépés"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"{}\n"
+"sorban álló partíciót tartalmaz, ez eltávolítja azokat, biztos benne?"
+
+msgid "No audio server"
+msgstr "Nincs hang kiszolgáló"
+
+msgid "(default)"
+msgstr "(alapértelmezett)"
+
+msgid "Use ESC to skip"
+msgstr "Használja az ESC billentyűt a kihagyáshoz"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+"Használja a CTRL+C billentyűkombinációt az aktuális kijelölés visszaállításához\n"
+"\n"
+
+msgid "Copy to: "
+msgstr "Másolás ide: "
+
+msgid "Edit: "
+msgstr "Szerkesztés: "
+
+msgid "Key: "
+msgstr "Kulcs: "
+
+msgid "Edit {}: "
+msgstr "{} szerkesztése: "
+
+msgid "Add: "
+msgstr "Hozzáadás: "
+
+msgid "Value: "
+msgstr "Érték: "
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr "Kihagyhatja a meghajtó kiválasztását és a particionálást és bármilyen meghajtó-beállítást használhat, amely az /mnt könyvtárhoz van csatolva (kísérleti)"
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "Válassza ki az egyik lemezt, vagy hagyja ki ezt a lépést és használja az /mnt-t alapértelmezettként"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "Válassza ki, hogy mely partíciókat szeretné formázásra megjelölni:"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "HSM használata a titkosított meghajtó feloldásához"
+
+msgid "Device"
+msgstr "Eszköz"
+
+msgid "Size"
+msgstr "Méret"
+
+msgid "Free space"
+msgstr "Szabad terület"
+
+msgid "Bus-type"
+msgstr "Bus-típus"
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr "Meg kell adni egy root jelszót, vagy legalább 1 sudo jogosultsággal rendelkező felhasználót"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "Felhasználónév megadása (hagyja üresen a kihagyáshoz): "
+
+msgid "The username you entered is invalid. Try again"
+msgstr "A megadott felhasználónév érvénytelen. Próbálja újra"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "\"{}\" felhasználónak rendszergazdának (sudoer) kell lennie?"
+
+msgid "Select which partitions to encrypt"
+msgstr "Válassza ki mely partíciókat szeretné titkosítani"
+
+msgid "very weak"
+msgstr "nagyon gyenge"
+
+msgid "weak"
+msgstr "gyenge"
+
+msgid "moderate"
+msgstr "közepes"
+
+msgid "strong"
+msgstr "erős"
+
+msgid "Add subvolume"
+msgstr "Alkötet hozzáadása"
+
+msgid "Edit subvolume"
+msgstr "Alkötet szerkesztése"
+
+msgid "Delete subvolume"
+msgstr "Alkötet törlése"
+
+msgid "Configured {} interfaces"
+msgstr "{} konfigurált interfész"
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr "Ez a beállítás lehetővé teszi, hogy a telepítés során hány párhuzamos letöltés történhet"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"Adja meg az engedélyezni kívánt párhuzamos letöltések számát.\n"
+" (Adjon meg egy értéket 1 és {} között)\n"
+"Megjegyzés:"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Maximális érték : {} ({} párhuzamos letöltést tesz lehetővé és egyszerre {} letöltést enged meg)"
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr " - Minimális érték : 1 (1 párhuzamos letöltést tesz lehetővé és egyszerre 2 letöltést enged meg)"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr " - Letiltás/Alapértelmezett: 0 (Kikapcsolja a párhuzamos letöltést, egyszerre csak 1 letöltést tesz lehetővé)"
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr "Érvénytelen bemenet! Próbálja újra egy érvényes bemenettel [1-től {max_downloads}-ig, vagy 0-t a letiltáshoz]"
+
+msgid "Parallel Downloads"
+msgstr "Párhuzamos letöltések"
+
+msgid "ESC to skip"
+msgstr "Használja az ESC billentyűt a kihagyáshoz"
+
+msgid "CTRL+C to reset"
+msgstr "Használja a CTRL+C billentyűkombinációt a visszaállításhoz"
+
+msgid "TAB to select"
+msgstr "Használja a TAB billentyűt a kiválasztáshoz"
+
+msgid "[Default value: 0] > "
+msgstr "[Alapértelmezett érték: 0] > "
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr "A fordítás használatához telepítsen manuálisan egy olyan betűtípust, amelyik támogatja ezt a nyelvet."
+
+msgid "The font should be stored as {}"
+msgstr "A betűtípust úgy kell eltárolni mint {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Az Archtelepítő futtatásához root jogosultságok szükségesek. További információkért tekintse meg a --help menüpontot."
+
+msgid "Select an execution mode"
+msgstr "Válasszon ki egy végrehajtási módot"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Nem sikerült lekérni a profilt a megadott URL-cím-ről: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "A profiloknak egyedi névvel kell rendelkezniük, de ismétlődő névvel rendelkező profildefiníciók találhatóak: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Válasszon ki egy vagy több eszközt a használathoz és a konfiguráláshoz"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Ha visszaállítja az eszköz-kiválasztást, akkor az aktuális lemezelrendezést is visszaállítja. Biztos benne?"
+
+msgid "Existing Partitions"
+msgstr "Meglévő partíciók"
+
+msgid "Select a partitioning option"
+msgstr "Válasszon egy particionálási lehetőséget"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Adja meg a csatolt eszközök gyökérkönyvtárát: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "A /home partíció minimális kapacitása: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Az Arch Linux partíció minimális kapacitása: {} GB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Ez az előre programozott profiles_bck lista, ami megkönnyítheti az olyan dolgok telepítését, mint például az asztali környezetekét"
+
+msgid "Current profile selection"
+msgstr "Aktuális profil kiválasztása"
+
+msgid "Remove all newly added partitions"
+msgstr "Az összes újonnan hozzáadott partíció eltávolítása"
+
+msgid "Assign mountpoint"
+msgstr "Csatolási pont hozzárendelése"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Megjelölés/megjelölés-visszavonása formázandóként (adatok törlése)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Megjelölés/megjelölés-visszavonása mint rendszerindító"
+
+msgid "Change filesystem"
+msgstr "Fájlrendszer módosítása"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Megjelölés/megjelölés-visszavonása mint tömörített"
+
+msgid "Set subvolumes"
+msgstr "Alkötetek beállítása"
+
+msgid "Delete partition"
+msgstr "Partíció törlése"
+
+msgid "Partition"
+msgstr "Partíció"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Ez a partíció jelenleg titkosított, a formázásához meg kell adni egy fájlrendszert"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "A partíció csatolási pontok a telepítésen belülre vonatkoznak, a boot pédául /boot lesz."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Ha a /boot csatolási pont be van állítva, akkor a partíció is rendszerbetöltőnek lesz jelölve."
+
+msgid "Mountpoint: "
+msgstr "Csatolási pont: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Jelenlegi szabad szektorok a(z) {} eszközön:"
+
+msgid "Total sectors: {}"
+msgstr "Összes szektor: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Adja meg a kezdőszektort (alapértelmezett: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Adja meg a partíció végszektorát (százalékban vagy blokkszámban, alapértelmezett: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Ezzel eltávolítja az összes újonnan hozzáadott partíciót, folytatja?"
+
+msgid "Partition management: {}"
+msgstr "Partíciókezelés: {}"
+
+msgid "Total length: {}"
+msgstr "Teljes hossz: {}"
+
+msgid "Encryption type"
+msgstr "Titkosítás típusa"
+
+msgid "Partitions"
+msgstr "Partíciók"
+
+msgid "No HSM devices available"
+msgstr "Nem állnak rendelkezésre HSM-eszközök"
+
+msgid "Partitions to be encrypted"
+msgstr "Titkosítandó partíciók"
+
+msgid "Select disk encryption option"
+msgstr "Válasszon ki egy lemeztitkosítási lehetőséget"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "Válassza ki a HSM-hez használni kívánt FIDO2-eszközt"
+
+msgid "Use a best-effort default partition layout"
+msgstr "A lehető legjobb beállítást lehetővé tévő alapértelmezett partíció-elrendezés használata"
+
+msgid "Manual Partitioning"
+msgstr "Kézi particionálás"
+
+msgid "Pre-mounted configuration"
+msgstr "Előcsatolt konfiguráció"
+
+msgid "Unknown"
+msgstr "Ismeretlen"
+
+msgid "Partition encryption"
+msgstr "Partíció titkosítás"
+
+msgid " ! Formatting {} in "
+msgstr " ! {} formázása erre: "
+
+msgid "↠Back"
+msgstr "↠Vissza"
+
+msgid "Disk encryption"
+msgstr "Lemez titkosítás"
+
+msgid "Configuration"
+msgstr "Konfiguráció"
+
+msgid "Password"
+msgstr "Jelszó"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "Minden beállítás visszaállításra kerül alaphelyzetre, biztos benne?"
+
+msgid "Back"
+msgstr "Vissza"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Válassza ki, hogy melyik bejelentkezési segédet szeretné telepíteni a kiválasztott profilokhoz: {}"
+
+msgid "Environment type: {}"
+msgstr "Környezet típusa: {}"
+
+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 "Az Sway nem támogatja az Nvidia saját fejlesztésű illesztőprogramját. Valószínű, hogy problémákba fog ütközni, rendben van ez így?"
+
+msgid "Installed packages"
+msgstr "Telepített csomagok"
+
+msgid "Add profile"
+msgstr "Profil hozzáadása"
+
+msgid "Edit profile"
+msgstr "Profil szerkesztése"
+
+msgid "Delete profile"
+msgstr "Profil törlése"
+
+msgid "Profile name: "
+msgstr "Profil neve: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "A megadott profilnév már használatban van. Próbálja újra"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "A profilhoz telepítendő csomagok (szóközzel elválasztva, vagy hagyja üresen a kihagyáshoz): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "A profillal engedélyezendő szolgáltatások (szóközzel elválasztva, vagy hagyja üresen a kihagyáshoz): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Engedélyezi ezt a profilt a telepítéshez?"
+
+msgid "Create your own"
+msgstr "Hozza létre a sajátját"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"Válasszon ki egy grafikus illesztőprogramot, vagy hagyja üresen az összes nyílt forráskódú illesztőprogram telepítéséhez"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Az Sway-nek hozzáférésre van szüksége (olyan hardvereszközök gyűjteményéhez mint például a billentyűzet, az egér, stb.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Válasszon egy opciót, hogy engedélyezze az Sway számára a hardverekhez való hozzáférést"
+
+msgid "Graphics driver"
+msgstr "Grafikus illesztőprogram"
+
+msgid "Greeter"
+msgstr "Bejelentkezési segéd"
+
+msgid "Please chose which greeter to install"
+msgstr "Válassza ki, hogy melyik bejelentkezési segédet szeretné telepíteni"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Ez az előre programozott default_profiles listája"
+
+msgid "Disk configuration"
+msgstr "Lemez konfiguráció"
+
+msgid "Profiles"
+msgstr "Profilok"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Lehetséges könyvtárak keresése a konfigurációs fájlok mentéséhez..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Válassza ki a könyvtárat (vagy könyvtárakat) a konfigurációs fájlok mentéséhez"
+
+msgid "Add a custom mirror"
+msgstr "Egyéni tükör hozzáadása"
+
+msgid "Change custom mirror"
+msgstr "Egyéni tükör megváltoztatása"
+
+msgid "Delete custom mirror"
+msgstr "Egyéni tükör törlése"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Adjon meg egy nevet (vagy hagyja üresen a kihagyáshoz): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Adjon megy egy URL-címet (vagy hagyja üresen a kihagyáshoz): "
+
+msgid "Select signature check option"
+msgstr "Válasszon egy aláírás-ellenőrzési lehetőséget"
+
+msgid "Select signature option"
+msgstr "Válasszon egy aláírás beállítást"
+
+msgid "Custom mirrors"
+msgstr "Egyedi tükrök"
+
+msgid "Defined"
+msgstr "Meghatározott"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Felhasználói konfiguráció mentése (beleértve a lemezelrendezést is)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Adjon meg egy könyvtárat a menteni kívánt konfiguráció(k) számára (a lap kitöltés engedélyezve)\n"
+"Mentési könyvtár: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Szeretné elmenteni a(z) {} konfigurációs fájlt a következő helyre?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "{} konfigurációs fájl mentése ide: {}"
+
+msgid "Mirrors"
+msgstr "Tükrök"
+
+msgid "Mirror regions"
+msgstr "Tükör régiók"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Maximális érték : {} ( {} párhuzamos letöltést tesz lehetővé és egyszerre {max_downloads+1} letöltést enged meg )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Érvénytelen bemenet! Próbálja újra egy érvényes bemenettel [1-től {}-ig, vagy 0-t a letiltáshoz]"
+
+msgid "Locales"
+msgstr "Nyelvi beállítások"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "A hálózatkezelő használata (szükséges az internet grafikus konfigurálásához GNOME-ban és KDE-ben)"
+
+msgid "Total: {} / {}"
+msgstr "Összes: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Minden beírt értéket mértékegységekkel kell ellátni: B, KB, KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Ha nincs mértékegység megadva, akkor az értéket szektorokként értelmezi"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Kezdőpont megadása (alapértelmezett: {} szektor): "
+
+msgid "Enter end (default: {}): "
+msgstr "Végpont megadása (alapértelmezett: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Nem sikerült meghatározni a fido2 eszközöket. Telepítve van a libfido2?"
+
+msgid "Path"
+msgstr "Elérési útvonal"
+
+msgid "Manufacturer"
+msgstr "Gyártó"
+
+msgid "Product"
+msgstr "Termék"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Érvénytelen konfiguráció: {error}"
+
+msgid "Type"
+msgstr "Típus"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Ez a beállítás lehetővé teszi, hogy a csomagletöltések során hány párhuzamos letöltés történhet"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Adja meg az engedélyezni kívánt párhuzamos letöltések számát.\n"
+"\n"
+"Megjegyzés:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Maximális ajánlott érték : {} ( Egyszerre {} párhuzamos letöltést tesz lehetővé )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Letiltás/Alapértelmezett: 0 (Kikapcsolja a párhuzamos letöltést, egyszerre csak 1 letöltést tesz lehetővé)\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Érvénytelen bemenet! Próbálja újra egy érvényes bemenettel [vagy 0-t a letiltáshoz]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "A Hyprland-nek hozzáférésre van szüksége (olyan hardvereszközök gyűjteményéhez mint például a billentyűzet, az egér, stb.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Válasszon egy opciót, hogy engedélyezze a Hyprland számára a hardverekhez való hozzáférést"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Minden beírt értéket mértékegységgel kell ellátni: %, B, KB, KiB, MB, MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Szeretne egységesített kernelképeket (UKI) használni?"
+
+msgid "Unified kernel images"
+msgstr "Egységesített kernelképek (UKI)"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Várakozás az időszinkronizálás (timedatectl show) befejezésére."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Az időszinkronizálás nem fejeződik be, amíg várakozik - tekintse meg a dokumentációban a megoldásokat: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Az automatikus időszinkronizálásra való várakozás kihagyása (ez problémákat okozhat, ha az idő nincs szinkronban a telepítés során)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Várakozás az Arch Linux kulcstartó szinkronizálásának (archlinux-keyring-wkd-sync) befejezésére."
+
+msgid "Selected profiles: "
+msgstr "Kiválasztott profil: "
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Az időszinkronizálás nem fejeződik be, amíg várakozik - tekintse meg a dokumentációban a megoldásokat: https://archinstall.readthedocs.io/"
+
+msgid "Mark/Unmark as nodatacow"
+msgstr "Megjelölés/megjelölés-visszavonása mint nodatacow"
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Szeretne tömörítést használni vagy letiltani az adatmásolást írás közben?"
+
+msgid "Use compression"
+msgstr "Tömörítés használata"
+
+msgid "Disable Copy-on-Write"
+msgstr "Ãrás közbeni adatmásolás letiltása"
diff --git a/archinstall/locales/id/LC_MESSAGES/base.mo b/archinstall/locales/id/LC_MESSAGES/base.mo
index 6e707237..b4a35025 100644
--- a/archinstall/locales/id/LC_MESSAGES/base.mo
+++ b/archinstall/locales/id/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po
index 6cc19cbf..0079046c 100644
--- a/archinstall/locales/id/LC_MESSAGES/base.po
+++ b/archinstall/locales/id/LC_MESSAGES/base.po
@@ -62,7 +62,7 @@ msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan ko
msgid "Copy ISO network configuration to installation"
msgstr "Salin konfigurasi jaringan ISO ke instalasi"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Gunakan NetworkManager (diperlukan untuk mengkonfigurasi internet secara grafis di GNOME dan KDE)"
msgid "Select one network interface to configure"
@@ -793,18 +793,17 @@ msgstr "Interface {} dikonfigurasi"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Opsi ini memungkinkan jumlah unduhan paralel yang dapat terjadi selama instalasi"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Masukkan jumlah unduhan paralel yang akan diaktifkan.\n"
-" (Masukkan nilai antara 1 hingga {max_downloads})\n"
+" (Masukkan nilai antara 1 hingga {})\n"
"Catatan:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Nilai maksimum : {max_downloads} ( Memungkinkan {max_downloads} unduhan paralel, memungkinkan {max_downloads+1} unduhan sekaligus)"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan {} unduhan sekaligus)"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Nilai minimum : 1 (Mengizinkan 1 unduhan paralel, memungkinkan 2 unduhan sekaligus)"
@@ -812,9 +811,9 @@ msgstr " - Nilai minimum : 1 (Mengizinkan 1 unduhan paralel, memungkinkan 2 un
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
msgstr " - Nonaktifkan/Default: 0 (Menonaktifkan pengunduhan paralel, hanya mengizinkan 1 unduhan pada satu waktu)"
-#, python-brace-format
+#, fuzzy, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {max_downloads}, atau 0 untuk menonaktifkan]"
+msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]"
msgid "Parallel Downloads"
msgstr "Unduhan Paralel"
@@ -832,46 +831,468 @@ msgid "[Default value: 0] > "
msgstr "[Nilai default: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr "Untuk dapat menggunakan terjemahan ini, silakan instal font yang mendukung bahasa tersebut secara manual."
+msgstr ""
msgid "The font should be stored as {}"
-msgstr "Font harus disimpan sebagai {}"
+msgstr ""
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "Pilih tindakan untuk '{}'"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "Pilih satu atau lebih hard drive untuk digunakan dan dikonfigurasi"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Jika Anda mengatur ulang pilihan harddrive, ini juga akan mengatur ulang tata letak disk saat ini. Apakah Anda yakin?"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "Menambahkan partisi...."
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "Hapus partisi"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Masukkan direktori untuk konfigurasi yang akan disimpan: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Kapasitas minimum untuk partisi /home: {}GB\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Kapasitas minimum untuk partisi Arch Linux: {}GB"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Ini adalah daftar profil yang telah diprogram sebelumnya, mereka mungkin memudahkan untuk menginstal hal-hal seperti desktop environment"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "Tata letak partisi saat ini"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "Buat partisi baru"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "Tetapkan titik-mount untuk sebuah partisi"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Tandai/Hapus tanda partisi yang akan diformat (menghapus data)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "Tandai/Hapus tanda partisi sebagai terkompresi (hanya btrfs)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "Hapus subvolume"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "Hapus partisi"
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Mount point partisi relatif terhadap di dalam instalasi, boot akan menjadi /boot sebagai contoh."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "Bukan direktori yang valid: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Masukkan sektor akhir partisi (persentase atau nomor blok, mis: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+msgid "Total length: {}"
+msgstr ""
+
+#, fuzzy
msgid "Encryption type"
-msgstr "Tipe enkripsi"
+msgstr "Kata sandi enkripsi"
msgid "Partitions"
-msgstr "Partisi"
+msgstr ""
msgid "No HSM devices available"
-msgstr "Tidak ada perangkat HSM yang tersedia"
+msgstr ""
+#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Partisi yang akan dienkripsi"
+msgstr "Pilih partisi mana yang akan dienkripsi"
msgid "Select disk encryption option"
-msgstr "Pilih opsi enkripsi disk"
+msgstr ""
msgid "Select a FIDO2 device to use for HSM"
-msgstr "Pilih perangkat FID02 yang akan digunakan untuk HSM"
+msgstr ""
-msgid "All settings will be reset, are you sure?"
-msgstr "Semua pengaturan akan direset, apakah Anda yakin?"
+#, fuzzy
+msgid "Use a best-effort default partition layout"
+msgstr "Hapus semua drive yang dipilih dan gunakan upaya terbaik tata letak partisi default"
-msgid "Back"
-msgstr "Kembali"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "Konfigurasi manual"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "Tidak ada konfigurasi"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
+msgstr ""
msgid "Disk encryption"
-msgstr "Enkripsi disk"
+msgstr ""
+
+#, fuzzy
+msgid "Configuration"
+msgstr "Tidak ada konfigurasi"
+#, fuzzy
msgid "Password"
-msgstr "Kata sandi"
+msgstr "Kata sandi root"
-msgid "Partition encryption"
-msgstr "Enkripsi partisi"
+#, fuzzy
+msgid "All settings will be reset, are you sure?"
+msgstr "{} berisi partisi yang mengantri, ini akan menghapusnya, apakah Anda yakin?"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): "
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+#, fuzzy
+msgid "Installed packages"
+msgstr "Paket tambahan"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "Profil"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "Profil"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "Hapus interface"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "Profil"
+
+#, fuzzy
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Nama pengguna yang Anda masukkan tidak valid. Coba lagi"
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan kosong untuk dilewati): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan kosong untuk dilewati): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Pilih driver grafis atau biarkan kosong untuk menginstal semua driver open-source"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Masukkan sektor akhir partisi (persentase atau nomor blok, mis: {}): "
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "Tidak ada konfigurasi"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "Profil"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Pilih satu atau lebih hard drive untuk digunakan dan dikonfigurasi"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "Tambahkan pengguna"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "Masukkan nama pengguna (kosongkan untuk melewati): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "Masukkan nama pengguna (kosongkan untuk melewati): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "Pilih interface untuk ditambahkan"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "Pilih interface untuk ditambahkan"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "Simpan konfigurasi pengguna"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "Masukkan direktori untuk konfigurasi yang akan disimpan: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "Simpan konfigurasi"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "Wilayah mirror"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "Wilayah mirror"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan {} unduhan sekaligus)"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]"
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Gunakan NetworkManager (diperlukan untuk mengkonfigurasi internet secara grafis di GNOME dan KDE)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Konfigurasi manual"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Opsi ini memungkinkan jumlah unduhan paralel yang dapat terjadi selama instalasi"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Masukkan jumlah unduhan paralel yang akan diaktifkan.\n"
+" (Masukkan nilai antara 1 hingga {})\n"
+"Catatan:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan {} unduhan sekaligus)"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Nonaktifkan/Default: 0 (Menonaktifkan pengunduhan paralel, hanya mengizinkan 1 unduhan pada satu waktu)"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Apakah Anda ingin menggunakan swap di zram?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Hapus interface"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Tandai/Hapus tanda partisi sebagai terkompresi (hanya btrfs)"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Apakah Anda ingin menggunakan kompresi BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/it/LC_MESSAGES/base.mo b/archinstall/locales/it/LC_MESSAGES/base.mo
index f199746c..3976d1d6 100644
--- a/archinstall/locales/it/LC_MESSAGES/base.mo
+++ b/archinstall/locales/it/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po
index 00df1a3f..1fcb61e4 100644
--- a/archinstall/locales/it/LC_MESSAGES/base.po
+++ b/archinstall/locales/it/LC_MESSAGES/base.po
@@ -3,13 +3,13 @@ msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
-"Last-Translator: Giovanni Donisi <giovannidonisi0701@gmail.com>\n"
+"Last-Translator: Alessio Cuccovillo <alessio.cuccovillo.dev@gmail.com>\n"
"Language-Team: \n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.1.1\n"
+"X-Generator: Poedit 3.4.2\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] Un file di log è stato creato qui: {} {}"
@@ -51,7 +51,7 @@ msgid "Choose an audio server"
msgstr "Scegli un server audio"
msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
-msgstr "Solo pacchetti come base, base-devel, linux, linux-firmware, efibootmgr e pacchetti di profilo opzionali vengono installati."
+msgstr "Vengono installati solo pacchetti come base, base-devel, linux, linux-firmware, efibootmgr e pacchetti di profilo opzionali."
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
msgstr "Se desideri un browser web, come firefox o chromium, puoi specificarlo nel seguente prompt."
@@ -62,7 +62,7 @@ msgstr "Scrivi pacchetti aggiuntivi da installare (separati da spazi, lascia vuo
msgid "Copy ISO network configuration to installation"
msgstr "Copia la configurazione di rete ISO nell'installazione"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Usa NetworkManager (necessario per configurare graficamente Internet in GNOME e KDE)"
msgid "Select one network interface to configure"
@@ -97,10 +97,10 @@ msgid "Enter a desired filesystem type for the partition"
msgstr "Immettere un tipo di filesystem desiderato per la partizione"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Inserisci la posizione iniziale (in unità separate: s, GB, %, ecc.; impostazione predefinita: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Inserisci la posizione finale (in unità separate: s, GB, %, ecc.; es: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} contiene partizioni in coda, questo le rimuoverà, sei sicuro?"
@@ -121,10 +121,10 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Seleziona per indice quale partizione montare da dove"
+"Seleziona per indice dove montare quale partizione"
msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
-msgstr " * I punti di montaggio della partizione sono relativi all'interno dell'installazione, l'avvio sarebbe per esempio /boot."
+msgstr " * I punti di montaggio della partizione sono relativi all'interno dell'installazione, per esempio l'avvio sarebbe /boot."
msgid "Select where to mount partition (leave blank to remove mountpoint): "
msgstr "Seleziona dove montare la partizione (lascia vuoto per rimuovere il punto di montaggio): "
@@ -181,7 +181,7 @@ msgid "Select what you wish to do with the selected block devices"
msgstr "Seleziona cosa desideri fare con i dispositivi a blocchi selezionati"
msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
-msgstr "Questo è un elenco di profili preprogrammati, che potrebbero semplificare l'installazione di elementi come gli ambienti desktop"
+msgstr "Questo è un elenco di profili pre-programmati, che potrebbero semplificare l'installazione di elementi come gli ambienti desktop"
msgid "Select keyboard layout"
msgstr "Seleziona il layout della tastiera"
@@ -382,7 +382,7 @@ msgid "Password for user \"{}\": "
msgstr "Password per l'utente \"{}\": "
msgid "Verifying that additional packages exist (this might take a few seconds)"
-msgstr "Verificando l'esistenza dei pacchetti aggiuntivi (potrebbe richiedere alcuni secondi)"
+msgstr "Verifico l'esistenza dei pacchetti aggiuntivi (potrebbe richiedere alcuni secondi)"
msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
msgstr "Si desidera utilizzare la sincronizzazione automatica dell'ora (NTP) con i server orari predefiniti?\n"
@@ -445,7 +445,7 @@ msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate
msgstr "Pacman è già in esecuzione, in attesa di un massimo di 10 minuti per la sua terminazione."
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "Il lock di pacman preesistente non è mai terminato. Please clean up any existing pacman sessions before using archinstall."
+msgstr "Il lock di pacman preesistente non è mai terminato. Rimuovi ogni sessione pacman esistente prima di usare archinstall."
msgid "Choose which optional additional repositories to enable"
msgstr "Scegli quali repository aggiuntivi facoltativi abilitare"
@@ -573,10 +573,10 @@ msgid "Save"
msgstr "Salva"
msgid "Subvolume name :"
-msgstr "Nome del sottovolume :"
+msgstr "Nome del sottovolume:"
msgid "Select a mount point :"
-msgstr "Seleziona un punto di montaggio :"
+msgstr "Seleziona un punto di montaggio:"
msgid "Select the desired subvolume options "
msgstr "Selezionare le opzioni del sottovolume desiderate "
@@ -669,7 +669,7 @@ msgid "Press Enter to continue."
msgstr "Premi Invio per continuare."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "Vuoi eseguire il chroot nell'installazione appena creata ed fare la configurazione post-installazione?"
+msgstr "Vuoi eseguire il chroot nell'installazione appena creata e fare la configurazione post-installazione?"
msgid "Are you sure you want to reset this setting?"
msgstr "Sei sicuro di voler ripristinare questa impostazione?"
@@ -793,18 +793,17 @@ msgstr "Configurate {} interfacce"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Questa opzione consente di impostare il numero di download paralleli che possono avvenire durante l'installazione"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Inserisci il numero di download paralleli da abilitare.\n"
-" (Inserisci un valore compreso tra 1 e {max_downloads})\n"
+" (Inserisci un valore compreso tra 1 e {})\n"
"Nota:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Valore massimo : {max_downloads} ( Consente {max_downloads} download parallelo, consente {max_downloads+1} download alla volta )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Valore massimo : {} ( Consente {} download parallelo, consente {} download alla volta )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Valore minimo : 1 ( Consente 1 download parallelo, consente 2 download alla volta )"
@@ -837,45 +836,410 @@ msgstr "Per poter utilizzare questa traduzione, installa manualmente un font che
msgid "The font should be stored as {}"
msgstr "Il carattere dovrebbe essere memorizzato come {}"
-#, fuzzy
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall richiede i privilegi di root per essere eseguito. Vedi —help per ulteriori informazioni."
+
+msgid "Select an execution mode"
+msgstr "Seleziona una modalità d’esecuzione"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Impossibile recuperare il profilo dall’URL specificato: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "I profili devono avere un nome univoco, ma sono state trovate definizioni di profilo con nome duplicato: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Selezionare uno o più dispositivi da utilizzare e configurare"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Se si ripristina la selezione del disco rigido, verrà ripristinato anche il layout del disco corrente. Sei sicuro?"
+
+msgid "Existing Partitions"
+msgstr "Partizioni esistenti"
+
+msgid "Select a partitioning option"
+msgstr "Selezione opzione di partizionamento"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Inserire la directory principale del dispositivo montato: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Capacità minima per la partizione /home: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Capacità minima per la partizione Arch Linux: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Questo è un elenco di profili preprogrammati, che potrebbero semplificare l'installazione di elementi come gli ambienti desktop"
+
+msgid "Current profile selection"
+msgstr "Selezione profilo corrente"
+
+msgid "Remove all newly added partitions"
+msgstr "Elimina tutte le partizioni appena aggiunte"
+
+msgid "Assign mountpoint"
+msgstr "Assegna punto di montaggio"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Seleziona/Deseleziona come da formattare (cancella i dati)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Contrassegna/Deseleziona come avviabile"
+
+msgid "Change filesystem"
+msgstr "Cambia filesystem"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Seleziona/Deseleziona come compressa"
+
+msgid "Set subvolumes"
+msgstr "Imposta sottovolumi"
+
+msgid "Delete partition"
+msgstr "Elimina partizione"
+
+msgid "Partition"
+msgstr "Partizione"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Questa partizione è attualmente crittografata, per formattarla è necessario specificare un filesystem"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "I punti di montaggio della partizione sono relativi all'interno dell'installazione, l'avvio sarebbe per esempio /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Se è impostato mountpoint /boot, anche la partizione sarà contrassegnata come avviabile."
+
+msgid "Mountpoint: "
+msgstr "Punto di montaggio: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Settori attualmente liberi sul dispositivo {}:"
+
+msgid "Total sectors: {}"
+msgstr "Settori totali: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Inserisci il settore iniziale (predefinito: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Inserisci il settore finale della partizione (percentuale o numero di blocco, predefinito: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Questo rimuoverà tutte le partizioni appena aggiunte, continuare?"
+
+msgid "Partition management: {}"
+msgstr "Gestione partizione: {}"
+
+msgid "Total length: {}"
+msgstr "Lunghezza totale: {}"
+
msgid "Encryption type"
-msgstr "Password di crittografia"
+msgstr "Tipo di crittografia"
msgid "Partitions"
-msgstr ""
+msgstr "Partizioni"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Nessun dispositivo HSM disponibile"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Seleziona le partizioni da crittografare"
+msgstr "Partizioni da crittografare"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "Selezione opzione per crittografia disco"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Seleziona un dispositivo FIDO2 da utilizzare per HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Utilizzare un layout di partizione predefinito ottimale"
+
+msgid "Manual Partitioning"
+msgstr "Partizionamento manuale"
+
+msgid "Pre-mounted configuration"
+msgstr "Configurazione pre caricata"
+
+msgid "Unknown"
+msgstr "Sconosciuto"
+
+msgid "Partition encryption"
+msgstr "Crittografia partizione"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formattazione {} in "
+
+msgid "↠Back"
+msgstr "↠Indietro"
+
+msgid "Disk encryption"
+msgstr "Crittografia disco"
+
+msgid "Configuration"
+msgstr "Configurazione"
+
+msgid "Password"
+msgstr "Password"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} contiene partizioni in coda, questo le rimuoverà, sei sicuro?"
+msgstr "Tutte le impostazioni verranno resettate, sei sicuro?"
msgid "Back"
+msgstr "Indietro"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Scegli quale messaggio di benvenuto installare per i profili scelti: {}"
+
+msgid "Environment type: {}"
+msgstr "Tipo di ambiente: {}"
+
+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 "Il driver proprietario Nvidia non è supportato da Sway. È probabile che incontrerai dei problemi, ti va bene?"
+
+msgid "Installed packages"
+msgstr "Pacchetti installati"
+
+msgid "Add profile"
+msgstr "Aggiungi profilo"
+
+msgid "Edit profile"
+msgstr "Modifica profilo"
+
+msgid "Delete profile"
+msgstr "Elimina profilo"
+
+msgid "Profile name: "
+msgstr "Profilo: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Il nome utente inserito non è già in uso. Riprova"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Pacchetti da installare con questo profilo (separati da spazi, lascia vuoto per saltare): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Servizi da abilitare con questo profilo (separati da spazi, lascia vuoto per saltare): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Questo profilo dovrebbe essere abilitato per l’installazione?"
+
+msgid "Create your own"
+msgstr "Crea il tuo"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"Seleziona un driver grafico o lascia vuoto per installare tutti i driver open source"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway ha bisogno dell’accesso al tuo posto (insieme di dispositivi hardware, ad esempio tastiera, mouse, ecc.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Scegli un’opzione per concedere a Sway l’accesso al tuo hardware"
-#, fuzzy
-msgid "Password"
-msgstr "Password di root"
+msgid "Graphics driver"
+msgstr "Driver grafici"
-msgid "Partition encryption"
+msgid "Greeter"
+msgstr "Programma di benvenuto"
+
+msgid "Please chose which greeter to install"
+msgstr "Scegli quale programma di benvenuto installare"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Questo è un elenco di default_profiles preprogrammati"
+
+msgid "Disk configuration"
+msgstr "Configurazione disco"
+
+msgid "Profiles"
+msgstr "Profili"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Ricerca di possibili directory in cui salvare i file di configurazione …"
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Selezionare uno o più directory per salvare i file di configurazione"
+
+msgid "Add a custom mirror"
+msgstr "Aggiungi un mirror personalizzato"
+
+msgid "Change custom mirror"
+msgstr "Cambia mirror personalizzato"
+
+msgid "Delete custom mirror"
+msgstr "Elimina mirror personalizzato"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Inserisci il nome (lascia vuoto per saltare): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Inserisci url (lascia vuoto per saltare): "
+
+msgid "Select signature check option"
+msgstr "Seleziona opzione di controllo della firma"
+
+msgid "Select signature option"
+msgstr "Seleziona opzioni di firma"
+
+msgid "Custom mirrors"
+msgstr "Mirror personalizzati"
+
+msgid "Defined"
+msgstr "Definito"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Salva configurazione utente (incluso layout del disco)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
msgstr ""
+"Immettere una directory per le configurazioni da salvare: (completamento col tasto tab abilitato)\n"
+"Directory di salvataggio: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Vuoi salvare {} file di configurazione al seguente percorso?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Salva file di configurazione {} in {}"
+
+msgid "Mirrors"
+msgstr "Mirrors"
+
+msgid "Mirror regions"
+msgstr "Regione dei mirror"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Valore massimo : {} ( Consente {} download paralleli, consente {max_downloads+1} downloads alla vola )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Input non valido! Riprova con un input valido [da 1 a {}, o 0 per disabilitare]."
+
+msgid "Locales"
+msgstr "Località"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Usa NetworkManager (necessario per configurare graficamente Internet in GNOME e KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Totale: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Tutti i valori immessi possono avere come suffisso un’unità: %, B, KB, KiB, MB, MiB…"
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Se non viene fornita alcuna unità, il valore viene interpretato come settori"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Inserire inizio (predefinito: {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Inserire fine (predefinito: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Impossibile determinare i dispositivi fido2. libfido2 è installato?"
+
+msgid "Path"
+msgstr "Percorso"
+
+msgid "Manufacturer"
+msgstr "Costruttore"
+
+msgid "Product"
+msgstr "Prodotto"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configurazione non valida: {error}"
+
+msgid "Type"
+msgstr "Tipo"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Questa opzione consente di impostare il numero di download paralleli che possono avvenire durante l'installazione"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Inserisci il numero di download paralleli da abilitare.\n"
+"\n"
+"Nota:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Valore massimo raccomandato : {} ( Consente {} download paralleli)"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Disabilita/Predefinito : 0 ( Disabilita il download parallelo, consente solo 1 download alla volta )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Input non valido! Riprova con un input valido [0 per disabilitare]."
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland ha bisogno dell’accesso al tuo posto (insieme di dispositivi hardware, ad esempio tastiera, mouse, ecc.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Scegliere u’opzione per dare accesso al tuo hardware a Hyprland"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Tutti i valori immessi possono avere come suffisso un’unità: %, B, KB, KiB, MB, MiB…"
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Vorresti usare swap su zram?"
+
+msgid "Unified kernel images"
+msgstr "Immagini kernel unificate"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "In attesa del completamento della sincronizzazione dell’orario (timedatectl show)"
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "La sincronizzazione dell’orario non si sta completando, mentre aspetti leggi la documentazione alla ricerca di una soluzione: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Salto l’attesa della sincronizzazione automatica dell’ora (potrebbe causare problemi se l’orario non è sincronizzato durante l’installazione)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "In attesa che la sincronizzazione del portachiavi di Arch Linux (archlinux-keyring-wkd-sync) sia completa."
+
+msgid "Selected profiles: "
+msgstr "Profili selezionati: "
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "La sincronizzazione dell’orario non si sta completando, in attesa, leggi la documentazione alla ricerca di una soluzione: https://archinstall.readthedocs.io/"
+
+msgid "Mark/Unmark as nodatacow"
+msgstr "Seleziona/Deseleziona come nodatacow"
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Vorresti usare la compressione o disabilitare CoW?"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Inserisci il settore iniziale (percentuale o numero di blocco, predefinito: {}): "
+msgid "Use compression"
+msgstr "Usa la compressione"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Inserisci il settore finale (percentuale o numero di blocco, predefinito: {}): "
+msgid "Disable Copy-on-Write"
+msgstr "Disabilita Copy-on-Write"
diff --git a/archinstall/locales/ja/LC_MESSAGES/base.mo b/archinstall/locales/ja/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..376186b1
--- /dev/null
+++ b/archinstall/locales/ja/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ja/LC_MESSAGES/base.po b/archinstall/locales/ja/LC_MESSAGES/base.po
new file mode 100644
index 00000000..cb63d0d4
--- /dev/null
+++ b/archinstall/locales/ja/LC_MESSAGES/base.po
@@ -0,0 +1,1299 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: UTUMI Hirosi <utuhiro78@yahoo.co.jp>\n"
+"Language-Team: \n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.4.2\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] ã“ã“ã«ãƒ­ã‚°ãƒ•ã‚¡ã‚¤ãƒ«ãŒä½œæˆã•ã‚Œã¾ã—ãŸ: {} {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " ã“ã®å•é¡Œï¼ˆãŠã‚ˆã³ãƒ•ã‚¡ã‚¤ãƒ«ï¼‰ã‚’ https://github.com/archlinux/archinstall/issues ã«é€ä¿¡ã—ã¦ãã ã•ã„"
+
+msgid "Do you really want to abort?"
+msgstr "本当ã«ä¸­æ­¢ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "And one more time for verification: "
+msgstr "確èªã®ãŸã‚ã«ã‚‚ã†1度: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "zram ã§ã‚¹ãƒ¯ãƒƒãƒ—を使用ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Desired hostname for the installation: "
+msgstr "インストール時ã®ãƒ›ã‚¹ãƒˆå: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "sudo 権é™ã‚’æŒã¤ã‚¹ãƒ¼ãƒ‘ーユーザーã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å: "
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "インストールã™ã‚‹è¿½åŠ ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ï¼ˆãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãªã„å ´åˆã¯ç„¡è¨˜å…¥ï¼‰: "
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚¹ãƒ¼ãƒ‘ーユーザーã«æ˜‡æ ¼ã—ã¾ã™ã‹ï¼ˆsudoer)?"
+
+msgid "Select a timezone"
+msgstr "タイムゾーンをé¸æŠž"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "systemd-boot ã®ä»£ã‚ã‚Šã« GRUB をブートローダーã¨ã—ã¦ä½¿ç”¨ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Choose a bootloader"
+msgstr "ブートローダーをé¸æŠž"
+
+msgid "Choose an audio server"
+msgstr "オーディオサーãƒãƒ¼ã‚’é¸æŠž"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "base, base-devel, linux, linux-firmware, efibootmgr ãªã©ã®ãƒ‘ッケージã¨ã‚ªãƒ—ションã®ãƒ—ロファイルパッケージã®ã¿ãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¾ã™ã€‚"
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr "firefox ã‚„ chromium ãªã©ã®ã‚¦ã‚§ãƒ–ブラウザーをインストールã™ã‚‹å ´åˆã¯ã€æ¬¡ã®ãƒ—ロンプトã§æŒ‡å®šã§ãã¾ã™ã€‚"
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "追加ã§ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã™ã‚‹ãƒ‘ッケージを書ã(スペースã§åŒºåˆ‡ã‚‹ã€‚無記入ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr "ISO ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šã‚’インストール環境ã«ã‚³ãƒ”ー"
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "NetworkManager を使用(GNOME 㨠KDE ã§ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã‚’グラフィカルã«è¨­å®šã™ã‚‹ã®ã«å¿…è¦ï¼‰"
+
+msgid "Select one network interface to configure"
+msgstr "設定ã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ã‚¤ã‚¹ã‚’ 1 ã¤é¸æŠž"
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr "ã©ã®ãƒ¢ãƒ¼ãƒ‰ã‚’ \"{}\" ã«è¨­å®šã™ã‚‹ã‹ã‚’é¸æŠžã€‚スキップã§ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆãƒ¢ãƒ¼ãƒ‰ \"{}\" を使用"
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "{} ã® IP ã¨ã‚µãƒ–ãƒãƒƒãƒˆã‚’入力(例: 192.168.0.5/24): "
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "ゲートウェイ(ルーター)㮠IP アドレスを入力。無ã„å ´åˆã¯ç„¡è¨˜å…¥: "
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr "DNS サーãƒãƒ¼ã‚’入力(スペースã§åŒºåˆ‡ã‚‹ã€‚ç„¡ã„å ´åˆã¯ç„¡è¨˜å…¥ï¼‰: "
+
+msgid "Select which filesystem your main partition should use"
+msgstr "メインパーティションã§ä½¿ç”¨ã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’é¸æŠž"
+
+msgid "Current partition layout"
+msgstr "ç¾åœ¨ã®ãƒ‘ーティションレイアウト"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"何をã™ã‚‹ã‹é¸æŠž\n"
+"{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "パーティションã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’入力"
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "開始場所を入力(å˜ä½: s, GB, % ãªã©ã€‚デフォルト: {}): "
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "終了場所を入力(å˜ä½: s, GB, % ãªã©ã€‚例: {}): "
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "{} ã«ã¯ã‚­ãƒ¥ãƒ¼ã«å…¥ã£ã¦ã„るパーティションãŒå«ã¾ã‚Œã¾ã™ã€‚ãれらãŒå‰Šé™¤ã•ã‚Œã¾ã™ãŒã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}\n"
+"\n"
+"削除ã™ã‚‹ãƒ‘ーティションをインデックスã§é¸æŠž"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}\n"
+"\n"
+"ã©ã®ãƒ‘ーティションをã©ã“ã«ãƒžã‚¦ãƒ³ãƒˆã™ã‚‹ã‹ã‚’インデックスã§é¸æŠž"
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "* パーティションã®ãƒžã‚¦ãƒ³ãƒˆãƒã‚¤ãƒ³ãƒˆã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«ãŠã‘る相対的ãªã‚‚ã®ã§ã‚ã‚Šã€ä¾‹ã¨ã—㦠boot 㯠/boot ã«ãªã‚Šã¾ã™ã€‚"
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr "パーティションをマウントã™ã‚‹å ´æ‰€ã‚’é¸æŠžï¼ˆç„¡è¨˜å…¥ã§ãƒžã‚¦ãƒ³ãƒˆãƒã‚¤ãƒ³ãƒˆã‚’削除): "
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"{}\n"
+"\n"
+"フォーマット対象ã¨ã—ã¦ãƒžãƒ¼ã‚¯ã™ã‚‹ãƒ‘ーティションをé¸æŠž"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"{}\n"
+"\n"
+"æš—å·åŒ–対象ã¨ã—ã¦ãƒžãƒ¼ã‚¯ã™ã‚‹ãƒ‘ーティションをé¸æŠž"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"{}\n"
+"\n"
+"ブータブルã¨ã—ã¦ãƒžãƒ¼ã‚¯ã™ã‚‹ãƒ‘ーティションをé¸æŠž"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"{}\n"
+"\n"
+"ファイルシステムを設定ã™ã‚‹ãƒ‘ーティションをé¸æŠž"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "パーティションã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’入力: "
+
+msgid "Archinstall language"
+msgstr "Archinstall ã®è¨€èªž"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr "é¸æŠžã—ãŸã™ã¹ã¦ã®ãƒ‰ãƒ©ã‚¤ãƒ–を消去ã—ã€ãƒ™ã‚¹ãƒˆã‚¨ãƒ•ã‚©ãƒ¼ãƒˆã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆãƒ‘ーティションレイアウトを使用ã™ã‚‹"
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr "個々ã®ãƒ‰ãƒ©ã‚¤ãƒ–ã‚’ã©ã†ã™ã‚‹ã‹ã‚’é¸æŠžï¼ˆæ¬¡ã«ãƒ‘ーティションã®ä½¿ç”¨æ–¹æ³•ãŒç¶šã)"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "é¸æŠžã—ãŸãƒ–ロックデãƒã‚¤ã‚¹ã§ä½•ã‚’ã™ã‚‹ã‹ã‚’é¸æŠž"
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr "ã“ã‚Œã¯äº‹å‰ã«ãƒ—ログラムã•ã‚ŒãŸãƒ—ロファイルã®ãƒªã‚¹ãƒˆã§ã™ã€‚デスクトップ環境ãªã©ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒç°¡å˜ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™"
+
+msgid "Select keyboard layout"
+msgstr "キーボードレイアウトをé¸æŠž"
+
+msgid "Select one of the regions to download packages from"
+msgstr "パッケージをダウンロードã™ã‚‹åœ°åŸŸã‚’ 1 ã¤é¸æŠž"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "使用・設定ã™ã‚‹ 1 ã¤ä»¥ä¸Šã®ãƒãƒ¼ãƒ‰ãƒ‰ãƒ©ã‚¤ãƒ–ã‚’é¸æŠž"
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr "AMD ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¨ã®äº’æ›æ€§ã‚’最大é™ã«é«˜ã‚ã‚‹ã«ã¯ã€ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒ—ンソースã¾ãŸã¯ 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 "Intel ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¨ã®äº’æ›æ€§ã‚’最大é™ã«é«˜ã‚ã‚‹ã«ã¯ã€ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒ—ンソースã¾ãŸã¯ Intel オプションã®ã„ãšã‚Œã‹ã‚’使用ã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚\n"
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr "Nvidia ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¨ã®äº’æ›æ€§ã‚’最大é™ã«é«˜ã‚ã‚‹ã«ã¯ã€Nvidia 独自ã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã‚’使用ã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"グラフィックドライãƒãƒ¼ã‚’é¸æŠžã™ã‚‹ã‹ã€ç„¡è¨˜å…¥ã§ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒ—ンソースドライãƒãƒ¼ã‚’インストール"
+
+msgid "All open-source (default)"
+msgstr "ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒ—ンソース(デフォルト)"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "使用ã™ã‚‹ã‚«ãƒ¼ãƒãƒ«ã‚’é¸æŠžã€‚無記入ã§ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã® \"{}\""
+
+msgid "Choose which locale language to use"
+msgstr "使用ã™ã‚‹ãƒ­ã‚±ãƒ¼ãƒ«è¨€èªžã‚’é¸æŠž"
+
+msgid "Choose which locale encoding to use"
+msgstr "使用ã™ã‚‹ãƒ­ã‚±ãƒ¼ãƒ« エンコーディングをé¸æŠž"
+
+msgid "Select one of the values shown below: "
+msgstr "以下ã®å€¤ã®ã„ãšã‚Œã‹ã‚’é¸æŠž: "
+
+msgid "Select one or more of the options below: "
+msgstr "以下ã®ã‚ªãƒ—ションを 1 ã¤ä»¥ä¸Šé¸æŠž: "
+
+msgid "Adding partition...."
+msgstr "パーティションを追加...."
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr "続行ã™ã‚‹ã«ã¯ã€æœ‰åŠ¹ãª fs-type を入力ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚有効㪠fs-type ã«ã¤ã„ã¦ã¯ã€ `man parted` ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "エラー: URL \"{}\" ã®ãƒ—ロファイルをリストã™ã‚‹ã¨ã€æ¬¡ã®çµæžœãŒç™ºç”Ÿ:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "エラー: \"{}\" ã®çµæžœã‚’ JSON ã¨ã—ã¦ãƒ‡ã‚³ãƒ¼ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸ:"
+
+msgid "Keyboard layout"
+msgstr "キーボードレイアウト"
+
+msgid "Mirror region"
+msgstr "ミラーã®åœ°åŸŸ"
+
+msgid "Locale language"
+msgstr "ロケール言語"
+
+msgid "Locale encoding"
+msgstr "ロケールエンコーディング"
+
+msgid "Drive(s)"
+msgstr "ドライブ"
+
+msgid "Disk layout"
+msgstr "ディスクレイアウト"
+
+msgid "Encryption password"
+msgstr "æš—å·åŒ–パスワード"
+
+msgid "Swap"
+msgstr "スワップ"
+
+msgid "Bootloader"
+msgstr "ブートローダー"
+
+msgid "Root password"
+msgstr "root パスワード"
+
+msgid "Superuser account"
+msgstr "スーパーユーザーアカウント"
+
+msgid "User account"
+msgstr "ユーザーアカウント"
+
+msgid "Profile"
+msgstr "プロファイル"
+
+msgid "Audio"
+msgstr "オーディオ"
+
+msgid "Kernels"
+msgstr "カーãƒãƒ«"
+
+msgid "Additional packages"
+msgstr "追加パッケージ"
+
+msgid "Network configuration"
+msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®š"
+
+msgid "Automatic time sync (NTP)"
+msgstr "時刻ã®è‡ªå‹•åŒæœŸï¼ˆNTP)"
+
+msgid "Install ({} config(s) missing)"
+msgstr "インストール({} 個ã®è¨­å®šãŒã‚ã‚Šã¾ã›ã‚“)"
+
+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 ""
+"ãƒãƒ¼ãƒ‰ãƒ‰ãƒ©ã‚¤ãƒ–ã®é¸æŠžã‚’スキップã—ã€\n"
+"{} ã«ãƒžã‚¦ãƒ³ãƒˆã—ã¦ã„るドライブã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—を使用ã—ã¾ã™ï¼ˆå®Ÿé¨“的)\n"
+"警告: Archinstall ã¯ã“ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã®é©åˆæ€§ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¾ã›ã‚“\n"
+"続行ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Re-using partition instance: {}"
+msgstr "パーティションインスタンスã®å†åˆ©ç”¨: {}"
+
+msgid "Create a new partition"
+msgstr "æ–°ã—ã„パーティションを作æˆ"
+
+msgid "Delete a partition"
+msgstr "パーティションを削除"
+
+msgid "Clear/Delete all partitions"
+msgstr "ã™ã¹ã¦ã®ãƒ‘ーティションをクリア/削除"
+
+msgid "Assign mount-point for a partition"
+msgstr "パーティションã«ãƒžã‚¦ãƒ³ãƒˆãƒã‚¤ãƒ³ãƒˆã‚’割り当ã¦ã‚‹"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "フォーマットã™ã‚‹ãƒ‘ーティションã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除(データを消去)"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "æš—å·åŒ–ã™ã‚‹ãƒ‘ーティションã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "ブータブルパーティションã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除(/boot ã®å ´åˆã¯è‡ªå‹•ï¼‰"
+
+msgid "Set desired filesystem for a partition"
+msgstr "パーティションã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’設定"
+
+msgid "Abort"
+msgstr "中止"
+
+msgid "Hostname"
+msgstr "ホストå"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "未設定。手動ã§è¨­å®šã—ãªã„é™ã‚Šä½¿ç”¨ã§ãã¾ã›ã‚“"
+
+msgid "Timezone"
+msgstr "タイムゾーン"
+
+msgid "Set/Modify the below options"
+msgstr "以下ã®ã‚ªãƒ—ションを設定/変更"
+
+msgid "Install"
+msgstr "インストール"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"Esc ã§ã‚¹ã‚­ãƒƒãƒ—\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "パーティションレイアウトをæ案"
+
+msgid "Enter a password: "
+msgstr "パスワードを入力: "
+
+msgid "Enter a encryption password for {}"
+msgstr "{} ã®æš—å·åŒ–パスワードを入力"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "ディスクã®æš—å·åŒ–パスワードを入力(暗å·åŒ–ã—ãªã„å ´åˆã¯ç„¡è¨˜å…¥ï¼‰: "
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "sudo 権é™ã‚’æŒã¤ã‚¹ãƒ¼ãƒ‘ーユーザーを作æˆ: "
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "root パスワードを入力(root を無効ã«ã™ã‚‹å ´åˆã¯ç„¡è¨˜å…¥ï¼‰: "
+
+msgid "Password for user \"{}\": "
+msgstr "ユーザー \"{}\" ã®ãƒ‘スワード: "
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr "追加ã®ãƒ‘ッケージãŒå­˜åœ¨ã™ã‚‹ã“ã¨ã‚’確èªï¼ˆã“ã‚Œã«ã¯æ•°ç§’ã‹ã‹ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ï¼‰"
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr "デフォルトã®ã‚¿ã‚¤ãƒ ã‚µãƒ¼ãƒãƒ¼ã§æ™‚刻ã®è‡ªå‹•åŒæœŸï¼ˆNTP)を使用ã—ã¾ã™ã‹ï¼Ÿ\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 ""
+"NTP ãŒæ©Ÿèƒ½ã™ã‚‹ã«ã¯ã€ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚¯ãƒ­ãƒƒã‚¯ãŠã‚ˆã³ãã®ä»–ã®è¨­å®šå¾Œã®ã‚¹ãƒ†ãƒƒãƒ—ãŒå¿…è¦ã«ãªã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚\n"
+"詳細ã«ã¤ã„ã¦ã¯ Arch wiki を確èªã—ã¦ãã ã•ã„"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "ユーザーåを入力ã—ã¦è¿½åŠ ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’作æˆï¼ˆç„¡è¨˜å…¥ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "Use ESC to skip\n"
+msgstr "Esc ã§ã‚¹ã‚­ãƒƒãƒ—\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+"\n"
+"リストã‹ã‚‰ã‚ªãƒ–ジェクトをé¸æŠžã—ã€ãã®ã‚ªãƒ–ジェクトã§å®Ÿè¡Œã§ãるアクション㮠1 ã¤ã‚’é¸æŠž"
+
+msgid "Cancel"
+msgstr "キャンセル"
+
+msgid "Confirm and exit"
+msgstr "確èªã—ã¦çµ‚了"
+
+msgid "Add"
+msgstr "追加"
+
+msgid "Copy"
+msgstr "コピー"
+
+msgid "Edit"
+msgstr "編集"
+
+msgid "Delete"
+msgstr "削除"
+
+msgid "Select an action for '{}'"
+msgstr "'{}' ã¸ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’é¸æŠž"
+
+msgid "Copy to new key:"
+msgstr "æ–°ã—ã„キーã«ã‚³ãƒ”ー:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "ä¸æ˜Žãª NIC タイプ: {}。å¯èƒ½ãªå€¤ã¯ {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"ã“ã‚ŒãŒé¸æŠžã—ãŸè¨­å®šã§ã™:"
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "Pacman ã¯ã™ã§ã«å®Ÿè¡Œã•ã‚Œã¦ãŠã‚Šã€çµ‚了ã™ã‚‹ã¾ã§æœ€å¤§ 10 分間待機ã—ã¾ã™ã€‚"
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr "既存㮠pacman ã®ãƒ­ãƒƒã‚¯ãŒçµ‚了ã—ã¾ã›ã‚“ã§ã—ãŸã€‚Archinstall を使用ã™ã‚‹å‰ã«ã€æ—¢å­˜ã® pacman セッションをクリーンアップã—ã¦ãã ã•ã„。"
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "オプションã§æœ‰åŠ¹ã«ã™ã‚‹è¿½åŠ ãƒªãƒã‚¸ãƒˆãƒªã‚’é¸æŠž"
+
+msgid "Add a user"
+msgstr "ユーザーを追加"
+
+msgid "Change password"
+msgstr "パスワードを変更"
+
+msgid "Promote/Demote user"
+msgstr "ユーザーを昇格/é™æ ¼"
+
+msgid "Delete User"
+msgstr "ユーザーを削除"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"æ–°ã—ã„ユーザーを定義\n"
+
+msgid "User Name : "
+msgstr "ユーザーå: "
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "{} ã¯ã‚¹ãƒ¼ãƒ‘ーユーザーã«æ˜‡æ ¼ã—ã¾ã™ã‹ï¼ˆsudoer)?"
+
+msgid "Define users with sudo privilege: "
+msgstr "sudo 権é™ã‚’æŒã¤ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’定義: "
+
+msgid "No network configuration"
+msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šãªã—"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "Btrfs パーティションã«å¿…è¦ãªã‚µãƒ–ボリュームを設定"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}\n"
+"\n"
+"サブボリュームを設定ã™ã‚‹ãƒ‘ーティションをé¸æŠž"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "ç¾åœ¨ã®ãƒ‘ーティション㮠Btrfs サブボリュームを管ç†"
+
+msgid "No configuration"
+msgstr "設定ãªã—"
+
+msgid "Save user configuration"
+msgstr "ユーザー設定をä¿å­˜"
+
+msgid "Save user credentials"
+msgstr "ユーザーã®èªè¨¼æƒ…報をä¿å­˜"
+
+msgid "Save disk layout"
+msgstr "ディスクレイアウトをä¿å­˜"
+
+msgid "Save all"
+msgstr "ã™ã¹ã¦ä¿å­˜"
+
+msgid "Choose which configuration to save"
+msgstr "ä¿å­˜ã™ã‚‹è¨­å®šã‚’é¸æŠž"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "設定をä¿å­˜ã™ã‚‹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’入力: "
+
+msgid "Not a valid directory: {}"
+msgstr "有効ãªãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã§ã¯ã‚ã‚Šã¾ã›ã‚“: {}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "使用ã—ã¦ã„るパスワードã¯å¼±ã„よã†ã§ã™ã€"
+
+msgid "are you sure you want to use it?"
+msgstr "本当ã«ä½¿ç”¨ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Optional repositories"
+msgstr "オプションã®ãƒªãƒã‚¸ãƒˆãƒª"
+
+msgid "Save configuration"
+msgstr "設定をä¿å­˜"
+
+msgid "Missing configurations:\n"
+msgstr "設定ãŒå­˜åœ¨ã—ã¾ã›ã‚“:\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "root-パスワードã‹1人以上ã®ã‚¹ãƒ¼ãƒ‘ーユーザーを指定ã—ã¦ãã ã•ã„"
+
+msgid "Manage superuser accounts: "
+msgstr "スーパーユーザーアカウントを管ç†: "
+
+msgid "Manage ordinary user accounts: "
+msgstr "一般ユーザーã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’管ç†: "
+
+msgid " Subvolume :{:16}"
+msgstr " サブボリューム :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " マウント場所 {:16}"
+
+msgid " with option {}"
+msgstr " 次ã®ã‚ªãƒ—ション㧠{}"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+" æ–°ã—ã„サブボリュームã«å¿…è¦ãªå€¤ã‚’入力 \n"
+
+msgid "Subvolume name "
+msgstr "サブボリュームå "
+
+msgid "Subvolume mountpoint"
+msgstr "サブボリュームã®ãƒžã‚¦ãƒ³ãƒˆãƒã‚¤ãƒ³ãƒˆ"
+
+msgid "Subvolume options"
+msgstr "サブボリュームã®ã‚ªãƒ—ション"
+
+msgid "Save"
+msgstr "ä¿å­˜"
+
+msgid "Subvolume name :"
+msgstr "サブボリュームå:"
+
+msgid "Select a mount point :"
+msgstr "マウント ãƒã‚¤ãƒ³ãƒˆã‚’é¸æŠž:"
+
+msgid "Select the desired subvolume options "
+msgstr "サブボリュームã®ã‚ªãƒ—ションをé¸æŠž"
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "sudo 権é™ã‚’æŒã¤ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã§å®šç¾©: "
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] ã“ã“ã«ãƒ­ã‚°ãƒ•ã‚¡ã‚¤ãƒ«ãŒä½œæˆã•ã‚Œã¾ã—ãŸ: {}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "デフォルトã®è¨­å®šã§ Btrfs サブボリュームを使用ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "Btrfs ã®åœ§ç¸®ã‚’使用ã—ã¾ã™ã‹ï¼Ÿ"
+
+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"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "/home パーティションã®æœ€å°å®¹é‡: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "Arch Linux パーティションã®æœ€å°å®¹é‡: {}GB"
+
+msgid "Continue"
+msgstr "続行"
+
+msgid "yes"
+msgstr "ã¯ã„"
+
+msgid "no"
+msgstr "ã„ã„ãˆ"
+
+msgid "set: {}"
+msgstr "セット: {}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "手動設定ã¯ãƒªã‚¹ãƒˆã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
+
+msgid "No iface specified for manual configuration"
+msgstr "手動設定㫠iface ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr "自動 DHCP を使用ã—ãªã„手動 NIC 設定ã«ã¯ã€IP アドレスãŒå¿…è¦ã§ã™"
+
+msgid "Add interface"
+msgstr "インターフェイスを追加"
+
+msgid "Edit interface"
+msgstr "インターフェイスを編集"
+
+msgid "Delete interface"
+msgstr "インターフェイスを削除"
+
+msgid "Select interface to add"
+msgstr "追加ã™ã‚‹ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹ã‚’é¸æŠž"
+
+msgid "Manual configuration"
+msgstr "手動設定"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "圧縮ã™ã‚‹ãƒ‘ーティションã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除(Btrfs ã®ã¿ï¼‰"
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "使用ã—ã¦ã„るパスワードã¯å¼±ã„よã†ã§ã™ãŒã€æœ¬å½“ã«ä½¿ç”¨ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr "デスクトップ環境ã¨ã‚¿ã‚¤ãƒ«ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã®é¸æŠžã‚’æä¾›ã—ã¾ã™ã€‚e.g. gnome, kde, sway"
+
+msgid "Select your desired desktop environment"
+msgstr "デスクトップ環境をé¸æŠž"
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr "Arch Linux ã‚’å¿…è¦ã«å¿œã˜ã¦ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºã§ãã‚‹éžå¸¸ã«åŸºæœ¬çš„ãªã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ã™ã€‚"
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr "ã•ã¾ã–ã¾ãªã‚µãƒ¼ãƒãƒ¼ パッケージã®é¸æŠžè‚¢ã‚’æä¾›ã—ã¾ã™ã€‚e.g. httpd, nginx, mariadb"
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr "インストールã™ã‚‹ã‚µãƒ¼ãƒãƒ¼ã‚’é¸æŠžã€‚存在ã—ãªã„å ´åˆã¯æœ€å°é™ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒå®Ÿè¡Œã•ã‚Œã¾ã™"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr "最å°é™ã®ã‚·ã‚¹ãƒ†ãƒ ã¨ xorg ãŠã‚ˆã³ã‚°ãƒ©ãƒ•ã‚£ãƒƒã‚¯ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã‚’インストール。"
+
+msgid "Press Enter to continue."
+msgstr "Enter キーã§ç¶šè¡Œã—ã¾ã™ã€‚"
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr "æ–°ã—ã作æˆã—ãŸã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã« chroot ã—ã¦ã€ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«å¾Œã®è¨­å®šã‚’実行ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "ã“ã®è¨­å®šã‚’リセットã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "使用・設定ã™ã‚‹ 1 ã¤ä»¥ä¸Šã®ãƒãƒ¼ãƒ‰ãƒ‰ãƒ©ã‚¤ãƒ–ã‚’é¸æŠž\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "既存ã®è¨­å®šã‚’変更ã™ã‚‹ã¨ã€ãƒ‡ã‚£ã‚¹ã‚¯ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆãŒãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã™ï¼"
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr "ãƒãƒ¼ãƒ‰ãƒ‰ãƒ©ã‚¤ãƒ–ã®é¸æŠžã‚’リセットã™ã‚‹ã¨ã€ç¾åœ¨ã®ãƒ‡ã‚£ã‚¹ã‚¯ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã‚‚リセットã•ã‚Œã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Save and exit"
+msgstr "ä¿å­˜ã—ã¦çµ‚了"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"{}\n"
+"キューã«å…¥ã‚Œã‚‰ã‚ŒãŸãƒ‘ーティションãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚削除ã—ã¾ã™ãŒã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "No audio server"
+msgstr "オーディオサーãƒãƒ¼ãªã—"
+
+msgid "(default)"
+msgstr "(デフォルト)"
+
+msgid "Use ESC to skip"
+msgstr "Esc ã§ã‚¹ã‚­ãƒƒãƒ—"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+"Ctrl+C ã§ç¾åœ¨ã®é¸æŠžã‚’リセット\n"
+"\n"
+
+msgid "Copy to: "
+msgstr "コピー先: "
+
+msgid "Edit: "
+msgstr "編集: "
+
+msgid "Key: "
+msgstr "キー: "
+
+msgid "Edit {}: "
+msgstr "編集 {}: "
+
+msgid "Add: "
+msgstr "追加: "
+
+msgid "Value: "
+msgstr "値: "
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr "ドライブé¸æŠžã¨ãƒ‘ーティション作æˆã‚’スキップã—ã¦ã€/mnt ã«ãƒžã‚¦ãƒ³ãƒˆã—ã¦ã„るドライブã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—を使用ã§ãã¾ã™ï¼ˆå®Ÿé¨“的)"
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "ã„ãšã‚Œã‹ã®ãƒ‡ã‚£ã‚¹ã‚¯ã‚’é¸æŠžã™ã‚‹ã‹ã€ã‚¹ã‚­ãƒƒãƒ—ã—㦠/mnt をデフォルトã¨ã—ã¦ä½¿ç”¨"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "フォーマットã™ã‚‹ãƒ‘ーティションをé¸æŠž:"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "HSM を使用ã—ã¦æš—å·åŒ–ã•ã‚ŒãŸãƒ‰ãƒ©ã‚¤ãƒ–ã®ãƒ­ãƒƒã‚¯ã‚’解除"
+
+msgid "Device"
+msgstr "デãƒã‚¤ã‚¹"
+
+msgid "Size"
+msgstr "サイズ"
+
+msgid "Free space"
+msgstr "空ã容é‡"
+
+msgid "Bus-type"
+msgstr "Bus タイプ"
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr "root-パスワードã‹ã€1人以上㮠sudo 権é™ã‚’æŒã¤ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "ユーザーåを入力(無記入ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "The username you entered is invalid. Try again"
+msgstr "入力ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ç„¡åŠ¹ã§ã™ã€‚ã‚‚ã†1度やり直ã—ã¦ãã ã•ã„"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "\"{}\" ã¯ã‚¹ãƒ¼ãƒ‘ーユーザーã«æ˜‡æ ¼ã—ã¾ã™ã‹ï¼ˆsudo)?"
+
+msgid "Select which partitions to encrypt"
+msgstr "æš—å·åŒ–ã™ã‚‹ãƒ‘ーティションをé¸æŠž"
+
+msgid "very weak"
+msgstr "éžå¸¸ã«å¼±ã„"
+
+msgid "weak"
+msgstr "å¼±ã„"
+
+msgid "moderate"
+msgstr "中程度"
+
+msgid "strong"
+msgstr "å¼·ã„"
+
+msgid "Add subvolume"
+msgstr "サブボリュームを追加"
+
+msgid "Edit subvolume"
+msgstr "サブボリュームを編集"
+
+msgid "Delete subvolume"
+msgstr "サブボリュームを削除"
+
+msgid "Configured {} interfaces"
+msgstr "設定ã—㟠{} インターフェース"
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr "ã“ã®ã‚ªãƒ—ションã¯ã€ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸­ã«å®Ÿè¡Œã§ãる並列ダウンロードã®æ•°ã‚’有効ã«ã—ã¾ã™"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"有効ã«ã™ã‚‹ä¸¦åˆ—ダウンロードã®æ•°ã‚’入力ã—ã¦ãã ã•ã„。\n"
+" (1 ã‹ã‚‰ {} ã®å€¤ã‚’入力)\n"
+"注æ„:"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - 最大値 : {} ({} 個ã®ä¸¦åˆ—ダウンロードを許å¯ã—ã¦ã€1度㫠{} 個ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’許å¯ã™ã‚‹)"
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr " - 最å°å€¤ : 1 (1 個ã®ä¸¦åˆ—ダウンロードを許å¯ã—ã¦ã€1度㫠2 個ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’許å¯ã™ã‚‹)"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr " - 無効/デフォルト : 0 (並列ダウンロードを無効ã«ã—ã¦ã€1度㫠1 個ã®ã¿ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’許å¯ã™ã‚‹)"
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr "無効ãªå…¥åŠ›ã§ã™ï¼æœ‰åŠ¹ãªå…¥åŠ›ã‚’使用ã—ã¦å†è©¦è¡Œã—ã¦ãã ã•ã„ [1 - {max_downloads}ã€0 ã§ç„¡åŠ¹]"
+
+msgid "Parallel Downloads"
+msgstr "並行ダウンロード"
+
+msgid "ESC to skip"
+msgstr "Esc ã§ã‚¹ã‚­ãƒƒãƒ—"
+
+msgid "CTRL+C to reset"
+msgstr "Ctrl+C ã§ãƒªã‚»ãƒƒãƒˆ"
+
+msgid "TAB to select"
+msgstr "Tab ã§é¸æŠž"
+
+msgid "[Default value: 0] > "
+msgstr "[デフォルト値: 0] > "
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr "ã“ã®ç¿»è¨³ã‚’使用ã§ãるよã†ã«ã™ã‚‹ã«ã¯ã€ãã®è¨€èªžã‚’サãƒãƒ¼ãƒˆã™ã‚‹ãƒ•ã‚©ãƒ³ãƒˆã‚’手動ã§ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„。"
+
+msgid "The font should be stored as {}"
+msgstr "フォント㯠{} ã¨ã—ã¦ä¿å­˜ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall を実行ã™ã‚‹ã«ã¯ root 権é™ãŒå¿…è¦ã§ã™ã€‚詳細ã«ã¤ã„ã¦ã¯ --help ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
+
+msgid "Select an execution mode"
+msgstr "実行モードをé¸æŠž"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "指定ã•ã‚ŒãŸ URL ã‹ã‚‰ãƒ—ロファイルをå–å¾—ã§ãã¾ã›ã‚“: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "プロファイルã«ã¯ä¸€æ„ã®åå‰ãŒå¿…è¦ã§ã™ãŒã€é‡è¤‡ã—ãŸåå‰ã®ãƒ—ロファイル定義ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "使用・設定ã™ã‚‹ 1 ã¤ä»¥ä¸Šã®ãƒ‡ãƒã‚¤ã‚¹ã‚’é¸æŠž"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "デãƒã‚¤ã‚¹ã®é¸æŠžã‚’リセットã™ã‚‹ã¨ã€ç¾åœ¨ã®ãƒ‡ã‚£ã‚¹ã‚¯ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã‚‚リセットã•ã‚Œã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Existing Partitions"
+msgstr "既存ã®ãƒ‘ーティション"
+
+msgid "Select a partitioning option"
+msgstr "パーティション作æˆã®ã‚ªãƒ—ションをé¸æŠž"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "マウントã•ã‚ŒãŸãƒ‡ãƒã‚¤ã‚¹ã®ãƒ«ãƒ¼ãƒˆãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’入力: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "/home パーティションã®æœ€å°å®¹é‡: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Arch Linux パーティションã®æœ€å°å®¹é‡: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "ã“ã‚Œã¯äº‹å‰ã«ãƒ—ログラムã•ã‚ŒãŸãƒ—ロファイルã®ãƒªã‚¹ãƒˆã§ã™ã€‚デスクトップ環境ãªã©ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒç°¡å˜ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™"
+
+msgid "Current profile selection"
+msgstr "ç¾åœ¨ã®ãƒ—ロファイルã®é¸æŠž"
+
+msgid "Remove all newly added partitions"
+msgstr "æ–°ã—ã追加ã•ã‚ŒãŸãƒ‘ーティションをã™ã¹ã¦å‰Šé™¤"
+
+msgid "Assign mountpoint"
+msgstr "マウントãƒã‚¤ãƒ³ãƒˆã‚’割り当ã¦ã‚‹"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "フォーマット対象ã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除(データを消去)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "ブータブルã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除"
+
+msgid "Change filesystem"
+msgstr "ファイルシステムを変更"
+
+msgid "Mark/Unmark as compressed"
+msgstr "圧縮対象ã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除"
+
+msgid "Set subvolumes"
+msgstr "サブボリュームを設定"
+
+msgid "Delete partition"
+msgstr "パーティションを削除"
+
+msgid "Partition"
+msgstr "パーティション"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "ã“ã®ãƒ‘ーティションã¯ç¾åœ¨æš—å·åŒ–ã•ã‚Œã¦ã„ã¾ã™ã€‚フォーマットã™ã‚‹ã«ã¯ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’指定ã—ã¦ãã ã•ã„"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "パーティションã®ãƒžã‚¦ãƒ³ãƒˆãƒã‚¤ãƒ³ãƒˆã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«ãŠã‘る相対的ãªã‚‚ã®ã§ã€ä¾‹ã¨ã—㦠boot 㯠/boot ã«ãªã‚Šã¾ã™ã€‚"
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "マウントãƒã‚¤ãƒ³ãƒˆã« /boot ãŒè¨­å®šã•ã‚ŒãŸå ´åˆã€ãƒ‘ーティションã¯ãƒ–ータブルã¨ã—ã¦ãƒžãƒ¼ã‚¯ã•ã‚Œã¾ã™ã€‚"
+
+msgid "Mountpoint: "
+msgstr "マウントãƒã‚¤ãƒ³ãƒˆ: "
+
+msgid "Current free sectors on device {}:"
+msgstr "デãƒã‚¤ã‚¹ {} ã®ç¾åœ¨ã®ç©ºãセクター:"
+
+msgid "Total sectors: {}"
+msgstr "ç·ã‚»ã‚¯ã‚¿ãƒ¼æ•°: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "開始セクターを入力(デフォルト: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "パーティションã®çµ‚了セクターを入力(パーセンテージã‹ãƒ–ロック番å·ã€‚デフォルト: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "ã“ã‚Œã¯æ–°ã—ã追加ã•ã‚ŒãŸãƒ‘ーティションをã™ã¹ã¦å‰Šé™¤ã—ã¾ã™ã€‚続ã‘ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Partition management: {}"
+msgstr "パーティションを管ç†: {}"
+
+msgid "Total length: {}"
+msgstr "全体ã®ã‚µã‚¤ã‚º: {}"
+
+msgid "Encryption type"
+msgstr "æš—å·åŒ–ã®ã‚¿ã‚¤ãƒ—"
+
+msgid "Partitions"
+msgstr "パーティション"
+
+msgid "No HSM devices available"
+msgstr "利用å¯èƒ½ãª HSM デãƒã‚¤ã‚¹ãŒã‚ã‚Šã¾ã›ã‚“"
+
+msgid "Partitions to be encrypted"
+msgstr "æš—å·åŒ–ã™ã‚‹ãƒ‘ーティション"
+
+msgid "Select disk encryption option"
+msgstr "ディスクã®æš—å·åŒ–オプションをé¸æŠž"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "HSM ã«ä½¿ç”¨ã™ã‚‹ FIDO2 デãƒã‚¤ã‚¹ã‚’é¸æŠž"
+
+msgid "Use a best-effort default partition layout"
+msgstr "ベストエフォートã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆãƒ‘ーティションレイアウトを使用"
+
+msgid "Manual Partitioning"
+msgstr "手動ã§ãƒ‘ーティションを作æˆ"
+
+msgid "Pre-mounted configuration"
+msgstr "事å‰ã«ãƒžã‚¦ãƒ³ãƒˆã•ã‚ŒãŸè¨­å®š"
+
+msgid "Unknown"
+msgstr "ä¸æ˜Ž"
+
+msgid "Partition encryption"
+msgstr "パーティションã®æš—å·åŒ–"
+
+msgid " ! Formatting {} in "
+msgstr " ! {} ã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆã¾ã§ "
+
+msgid "↠Back"
+msgstr "↠戻る"
+
+msgid "Disk encryption"
+msgstr "ディスクã®æš—å·åŒ–"
+
+msgid "Configuration"
+msgstr "設定"
+
+msgid "Password"
+msgstr "パスワード"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "ã™ã¹ã¦ã®è¨­å®šãŒãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Back"
+msgstr "戻る"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "é¸æŠžã—ãŸãƒ—ロファイルã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã™ã‚‹ã‚°ãƒªãƒ¼ã‚¿ãƒ¼ã‚’é¸æŠž: {}"
+
+msgid "Environment type: {}"
+msgstr "環境ã®ã‚¿ã‚¤ãƒ—: {}"
+
+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 "プロプライエタリ㮠Nvidia ドライãƒãƒ¼ã¯ Sway ã§ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“。å•é¡ŒãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ãŒã€ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "Installed packages"
+msgstr "インストールã™ã‚‹ãƒ‘ッケージ"
+
+msgid "Add profile"
+msgstr "プロファイルを追加"
+
+msgid "Edit profile"
+msgstr "プロファイルを編集"
+
+msgid "Delete profile"
+msgstr "プロファイルを削除"
+
+msgid "Profile name: "
+msgstr "プロファイルå: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "入力ã—ãŸãƒ—ロファイルåã¯ã™ã§ã«ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ã€‚ã‚‚ã†1度やり直ã—ã¦ãã ã•ã„"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "ã“ã®ãƒ—ロファイルã§ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã™ã‚‹ãƒ‘ッケージ(スペースã§åŒºåˆ‡ã‚‹ã€‚無記入ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "ã“ã®ãƒ—ロファイルã§æœ‰åŠ¹ã«ã™ã‚‹ã‚µãƒ¼ãƒ“ス(スペースã§åŒºåˆ‡ã‚‹ã€‚未記入ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "ã“ã®ãƒ—ロファイルã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã‚’有効ã«ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Create your own"
+msgstr "自分ã§ä½œæˆ"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"グラフィックドライãƒãƒ¼ã‚’é¸æŠžã€‚無記入ã§ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒ—ンソースドライãƒãƒ¼ã‚’インストール"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway ã¯ã‚·ãƒ¼ãƒˆï¼ˆã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ã€ãƒžã‚¦ã‚¹ãªã©ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ãƒ‡ãƒã‚¤ã‚¹ã®é›†åˆï¼‰ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Sway ã«ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹ã‚ªãƒ—ションをé¸æŠž"
+
+msgid "Graphics driver"
+msgstr "グラフィックドライãƒãƒ¼"
+
+msgid "Greeter"
+msgstr "グリーター"
+
+msgid "Please chose which greeter to install"
+msgstr "インストールã™ã‚‹ã‚°ãƒªãƒ¼ã‚¿ãƒ¼ã‚’é¸æŠž"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "ã“ã‚Œã¯ã€äº‹å‰ã«ãƒ—ログラムã•ã‚ŒãŸ default_profile ã®ãƒªã‚¹ãƒˆã§ã™"
+
+msgid "Disk configuration"
+msgstr "ディスクã®è¨­å®š"
+
+msgid "Profiles"
+msgstr "プロファイル"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "設定ファイルをä¿å­˜ã§ãるディレクトリを検索ã—ã¦ã„ã¾ã™..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "設定ファイルをä¿å­˜ã™ã‚‹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é¸æŠž"
+
+msgid "Add a custom mirror"
+msgstr "カスタムミラーを追加"
+
+msgid "Change custom mirror"
+msgstr "カスタムミラーを変更"
+
+msgid "Delete custom mirror"
+msgstr "カスタムミラーを削除"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "åå‰ã‚’入力(無記入ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "URL を入力(未記入ã§ã‚¹ã‚­ãƒƒãƒ—): "
+
+msgid "Select signature check option"
+msgstr "ç½²åãƒã‚§ãƒƒã‚¯ã®ã‚ªãƒ—ションをé¸æŠž"
+
+msgid "Select signature option"
+msgstr "ç½²åオプションをé¸æŠž"
+
+msgid "Custom mirrors"
+msgstr "カスタムミラー"
+
+msgid "Defined"
+msgstr "定義済ã¿"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "ユーザー設定をä¿å­˜ï¼ˆãƒ‡ã‚£ã‚¹ã‚¯ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã‚’å«ã‚€ï¼‰"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"設定をä¿å­˜ã™ã‚‹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’入力(Tab ã§è£œå®Œå¯èƒ½ï¼‰\n"
+"ä¿å­˜ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"{} 設定ファイルを次ã®å ´æ‰€ã«ä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "{} 設定ファイルを {} ã«ä¿å­˜"
+
+msgid "Mirrors"
+msgstr "ミラー"
+
+msgid "Mirror regions"
+msgstr "ミラーã®åœ°åŸŸ"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - 最大値 : {}({} 個ã®ä¸¦åˆ—ダウンロードを許å¯ã—ã€1度㫠{max_downloads+1} 個ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’許å¯ã™ã‚‹ï¼‰"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "無効ãªå…¥åŠ›ã§ã™ï¼æœ‰åŠ¹ãªå…¥åŠ›ã§ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„ [1 ã‹ã‚‰ {}ã€ã¾ãŸã¯ 0 ã§ç„¡åŠ¹]"
+
+msgid "Locales"
+msgstr "ロケール"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager を使用(GNOME 㨠KDE ã§ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã‚’グラフィカルã«è¨­å®šã™ã‚‹ã®ã«å¿…è¦ï¼‰"
+
+msgid "Total: {} / {}"
+msgstr "全体: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "入力ã—ãŸã™ã¹ã¦ã®å€¤ã«ã€Bã€KBã€KiBã€MBã€MiB ãªã©ã®å˜ä½ã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "å˜ä½ãŒæŒ‡å®šã•ã‚Œã¦ã„ãªã„å ´åˆã€å€¤ã¯ã‚»ã‚¯ã‚¿ãƒ¼ã¨ã—ã¦è§£é‡ˆã•ã‚Œã¾ã™"
+
+msgid "Enter start (default: sector {}): "
+msgstr "開始値を入力(デフォルト: セクター {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "終了値を入力(デフォルト: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "fido2 デãƒã‚¤ã‚¹ã‚’特定ã§ãã¾ã›ã‚“。lifido2 ã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Path"
+msgstr "パス"
+
+msgid "Manufacturer"
+msgstr "メーカー"
+
+msgid "Product"
+msgstr "製å“"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "無効ãªè¨­å®š: {error}"
+
+msgid "Type"
+msgstr "タイプ"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "ã“ã®ã‚ªãƒ—ションã¯ã€ãƒ‘ッケージã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ä¸­ã«å®Ÿè¡Œã§ãる並列ダウンロードã®æ•°ã‚’設定ã—ã¾ã™"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"並列ダウンロードã®æ•°ã‚’入力ã—ã¦ãã ã•ã„。\n"
+"\n"
+"注æ„:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - 最大推奨値 : {} (1度㫠{} 個ã®ä¸¦åˆ—ダウンロードを許å¯ã™ã‚‹ï¼‰"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - 無効/デフォルト : 0 (並列ダウンロードを無効ã«ã—ã¦ã€1度㫠1 個ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã®ã¿è¨±å¯ã™ã‚‹ï¼‰\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "無効ãªå…¥åŠ›ã§ã™ï¼æœ‰åŠ¹ãªå…¥åŠ›ã§ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„(無効ã«ã™ã‚‹å ´åˆã¯ 0)"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland ã¯ã‚ãªãŸã®ã‚·ãƒ¼ãƒˆï¼ˆã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ã€ãƒžã‚¦ã‚¹ãªã©ã®ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ãƒ‡ãƒã‚¤ã‚¹ã®é›†åˆï¼‰ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Hyprland ã«ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹ã‚ªãƒ—ションをé¸æŠž"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "入力ã—ãŸã™ã¹ã¦ã®å€¤ã«ã€%ã€Bã€KBã€KiBã€MBã€MiB ãªã©ã®å˜ä½ã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Unified カーãƒãƒ«ã‚¤ãƒ¡ãƒ¼ã‚¸ã‚’使用ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Unified kernel images"
+msgstr "Unified カーãƒãƒ«ã‚¤ãƒ¡ãƒ¼ã‚¸"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "時刻ã®åŒæœŸï¼ˆtimedatectl show)ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…æ©Ÿã—ã¦ã„ã¾ã™ã€‚"
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "待機中ã«æ™‚刻åŒæœŸãŒå®Œäº†ã—ã¾ã›ã‚“ - 回é¿ç­–ã«ã¤ã„ã¦ã¯ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’確èªã—ã¦ãã ã•ã„: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "自動ã§ã®æ™‚刻åŒæœŸã®å¾…機をスキップã—ã¾ã™ï¼ˆã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸­ã«æ™‚刻ãŒåŒæœŸã—ã¦ã„ãªã„å ´åˆã¯ã€å•é¡ŒãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ï¼‰"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Arch Linux キーリングã®åŒæœŸï¼ˆarchlinux-keyring-wkd-sync)ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™ã€‚"
+
+msgid "Selected profiles: "
+msgstr "é¸æŠžã—ãŸãƒ—ロファイル: "
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "待機中ã«æ™‚刻åŒæœŸãŒå®Œäº†ã—ã¾ã›ã‚“ - 回é¿ç­–ã«ã¤ã„ã¦ã¯ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’確èªã—ã¦ãã ã•ã„: https://archinstall.readthedocs.io/"
+
+msgid "Mark/Unmark as nodatacow"
+msgstr "nodatacow ã¨ã—ã¦ãƒžãƒ¼ã‚¯/マーク解除"
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr "圧縮を使用ã—ã¾ã™ã‹ã€ãã‚Œã¨ã‚‚ CoW を無効ã«ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Use compression"
+msgstr "圧縮ã™ã‚‹"
+
+msgid "Disable Copy-on-Write"
+msgstr "コピーオンライトを無効ã«ã™ã‚‹"
+
+msgid "Configuration type: {}"
+msgstr "設定タイプ: {}"
+
+msgid "LVM configuration type"
+msgstr "LVM ã®è¨­å®šã‚¿ã‚¤ãƒ—"
+
+msgid "LVM disk encryption with more than 2 partitions is currently not supported"
+msgstr "パーティションãŒ2個を超ãˆã‚‹å ´åˆã® LVM ディスク暗å·åŒ–ã¯ã€ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE Plasma)"
+msgstr "NetworkManager を使用ã™ã‚‹ï¼ˆGNOME 㨠KDE Plasma ã§ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã‚’グラフィカルã«è¨­å®šã™ã‚‹ã®ã«å¿…è¦ï¼‰"
+
+msgid "Select a LVM option"
+msgstr "LVM ã®ã‚ªãƒ—ションをé¸æŠž"
+
+msgid "Default layout"
+msgstr "デフォルトã®ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆ"
+
+msgid "No Encryption"
+msgstr "æš—å·åŒ–ãªã—"
+
+msgid "LUKS"
+msgstr "LUKS"
+
+msgid "LVM on LUKS"
+msgstr "LUKS 上㮠LVM"
+
+msgid "LUKS on LVM"
+msgstr "LVM 上㮠LUKS"
+
+msgid "Partitioning"
+msgstr "パーティションを作æˆ"
+
+msgid "Logical Volume Management (LVM)"
+msgstr "è«–ç†ãƒœãƒªãƒ¥ãƒ¼ãƒ ç®¡ç†ï¼ˆLVM)"
+
+msgid "Physical volumes"
+msgstr "物ç†ãƒœãƒªãƒ¥ãƒ¼ãƒ "
+
+msgid "Volumes"
+msgstr "ボリューム"
+
+msgid "LVM volumes"
+msgstr "LVM ボリューム"
+
+msgid "LVM volumes to be encrypted"
+msgstr "æš—å·åŒ–ã™ã‚‹ LVM ボリューム"
+
+msgid "Select which LVM volumes to encrypt"
+msgstr "æš—å·åŒ–ã™ã‚‹ LVM ボリュームをé¸æŠž"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. GNOME, KDE Plasma, Sway"
+msgstr "デスクトップ環境ã¨ã‚¿ã‚¤ãƒ«ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã®é¸æŠžã‚’æä¾›ã—ã¾ã™ã€‚例: GNOME, KDE Plasma, Sway"
diff --git a/archinstall/locales/ka/LC_MESSAGES/base.mo b/archinstall/locales/ka/LC_MESSAGES/base.mo
index b95a6e0e..04c94cd0 100644
--- a/archinstall/locales/ka/LC_MESSAGES/base.mo
+++ b/archinstall/locales/ka/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po
index c89ec795..0206e0ae 100644
--- a/archinstall/locales/ka/LC_MESSAGES/base.po
+++ b/archinstall/locales/ka/LC_MESSAGES/base.po
@@ -63,7 +63,7 @@ msgstr "დáƒáƒ›áƒáƒ¢áƒ”ბითი პáƒáƒ™áƒ”ტები დáƒáƒ¡áƒáƒ§á
msgid "Copy ISO network configuration to installation"
msgstr "ISO-ის ქსელის კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡ კáƒáƒžáƒ˜áƒ áƒ”ბრდáƒáƒ§áƒ”ნების დრáƒáƒ¡"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "NetworkManager-ის გáƒáƒ›áƒáƒ§áƒ”ნებრ(áƒáƒ£áƒªáƒ˜áƒšáƒ”ბელირინტერნეტის GNOME/KDE-დáƒáƒœ მáƒáƒ¡áƒáƒ áƒ’ებáƒáƒ“)"
msgid "Select one network interface to configure"
@@ -794,18 +794,17 @@ msgstr "მáƒáƒ áƒ’ებულირ{} ინტერფეისი"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "ეს პáƒáƒ áƒáƒ›áƒ”ტრი დáƒáƒ§áƒ”ნებისáƒáƒ¡ მითითებული რáƒáƒáƒ“ენáƒáƒ‘ის პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ დáƒáƒ£áƒ¨áƒ•áƒ”ბს"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"შეიყვáƒáƒœáƒ”თ დáƒáƒ¡áƒáƒ¨áƒ•áƒ”ბი პáƒáƒ áƒáƒšáƒ”ლური გáƒáƒ“მáƒáƒ¬áƒ”რების რáƒáƒáƒ“ენáƒáƒ‘áƒ.\n"
-" (შეიყვáƒáƒœáƒ”თ მნიშვნელáƒáƒ‘რ1-დáƒáƒœ {max_downloads}-მდე)\n"
+" (შეიყვáƒáƒœáƒ”თ მნიშვნელáƒáƒ‘რ1-დáƒáƒœ {}-მდე)\n"
"დáƒáƒ˜áƒ›áƒáƒ®áƒ¡áƒáƒ•áƒ áƒ”თ:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - მინიმáƒáƒšáƒ£áƒ áƒ˜ მნიშვნელáƒáƒ‘რ: {max_downloads} ( დáƒáƒ£áƒ¨áƒ•áƒ”ბს {max_downloads} პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡, დáƒáƒ£áƒ¨áƒ•áƒ”ბს {max_downloads+1} ერთდრáƒáƒ£áƒš გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - მინიმáƒáƒšáƒ£áƒ áƒ˜ მნიშვნელáƒáƒ‘რ: {} ( დáƒáƒ£áƒ¨áƒ•áƒ”ბს {} პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡, დáƒáƒ£áƒ¨áƒ•áƒ”ბს {} ერთდრáƒáƒ£áƒš გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - მინიმáƒáƒšáƒ£áƒ áƒ˜ მნიშვნელáƒáƒ‘რ: 1 ( დáƒáƒ£áƒ¨áƒ•áƒ”ბს 1 პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡, დáƒáƒ£áƒ¨áƒ•áƒ”ბს 2 ერთდრáƒáƒ£áƒš გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ )"
@@ -813,9 +812,9 @@ msgstr " - მინიმáƒáƒšáƒ£áƒ áƒ˜ მნიშვნელáƒáƒ‘რ:
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
msgstr " - გáƒáƒ›áƒáƒ áƒ—ვáƒ/ნáƒáƒ’ულისხმები : 0 ( პáƒáƒ áƒáƒšáƒ”ლური გáƒáƒ“მáƒáƒ¬áƒ”რების გáƒáƒ—იშვáƒ. დრáƒáƒ˜áƒ¡ ერთ მáƒáƒ›áƒ”ნტში მხáƒáƒšáƒáƒ“ ერთი გáƒáƒ“მáƒáƒ¬áƒ”რრმáƒáƒ®áƒ“ებრ)"
-#, python-brace-format
+#, fuzzy, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "შეყვáƒáƒœáƒ˜áƒšáƒ˜ რიცხვი áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ! თáƒáƒ•áƒ˜áƒ“áƒáƒœ სცáƒáƒ“ეთ [1-დáƒáƒœ {max_downloads}-მდე, áƒáƒœ 0, გáƒáƒ¡áƒáƒ—იშáƒáƒ“]"
+msgstr "შეყვáƒáƒœáƒ˜áƒšáƒ˜ რიცხვი áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ! თáƒáƒ•áƒ˜áƒ“áƒáƒœ სცáƒáƒ“ეთ [1-დáƒáƒœ {}-მდე, áƒáƒœ 0, გáƒáƒ¡áƒáƒ—იშáƒáƒ“]"
msgid "Parallel Downloads"
msgstr "პáƒáƒ áƒáƒšáƒ”ლური გáƒáƒ“მáƒáƒ¬áƒ”რები"
@@ -838,6 +837,126 @@ msgstr "თáƒáƒ áƒ’მáƒáƒœáƒ˜áƒ¡ გáƒáƒ›áƒáƒ¡áƒáƒ§áƒ”ნებლáƒáƒ“ á
msgid "The font should be stored as {}"
msgstr "ფáƒáƒœáƒ¢áƒ˜ {}-ში უნდრიყáƒáƒ¡ შენáƒáƒ®áƒ£áƒšáƒ˜"
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ ქმედებრ'{}'-სთვის"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ ერთი áƒáƒœ მეტი მყáƒáƒ áƒ˜ დისკი დრმáƒáƒ˜áƒ áƒ’ეთ"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "ეს მყáƒáƒ áƒ˜ დისკის áƒáƒ áƒ©áƒ”ვáƒáƒœáƒ¡ დრმიმდინáƒáƒ áƒ” დისკის გáƒáƒœáƒšáƒáƒ’ებáƒáƒ¡ სáƒáƒ¬áƒ§áƒ˜áƒ¡ მნიშვნელáƒáƒ‘ებზე დáƒáƒáƒ‘რუნებს. დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული ბრძáƒáƒœáƒ“ებით?"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ”ბი"
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ დისკის დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრი"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "შეიყვáƒáƒœáƒ”თ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე, სáƒáƒ“áƒáƒª კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜(ებ)-ი იქნებრშენáƒáƒ®áƒ£áƒšáƒ˜: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "მინიმáƒáƒšáƒ£áƒ áƒ˜ სივრცე დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡áƒ—ვის /home: {}გბ\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "მინიმáƒáƒšáƒ£áƒ áƒ˜ სივრცე ArchLinux-ის დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡áƒ—ვის: {}გბ"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "ეს წინáƒáƒ¡áƒ¬áƒáƒ  მითითებული პრáƒáƒ¤áƒ˜áƒšáƒ”ბის სიáƒáƒ. მáƒáƒ—ი დáƒáƒ®áƒ›áƒáƒ áƒ”ბით ისეთი რáƒáƒ›áƒ”ბის, რáƒáƒ’áƒáƒ áƒ˜áƒªáƒáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის გáƒáƒ áƒ”მáƒáƒ”ბი, დáƒáƒ§áƒ”ნებრუფრრáƒáƒ“ვილიáƒ"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "მიმდინáƒáƒ áƒ” დáƒáƒœáƒáƒ§áƒáƒ¤áƒ”ბის გáƒáƒœáƒšáƒáƒ’ებáƒ"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "áƒáƒ®áƒáƒšáƒ˜ დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ შექმნáƒ"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ მიმáƒáƒ’რების წერტილის მინიჭებáƒ"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ დáƒáƒ¡áƒáƒ¤áƒáƒ áƒ›áƒáƒ¢áƒ”ბლáƒáƒ‘ის ჭდის მáƒáƒ®áƒ¡áƒœáƒ/დáƒáƒ“ებრ(მáƒáƒœáƒáƒªáƒ”მები წáƒáƒ˜áƒ¨áƒšáƒ”ბáƒ)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ–ე შეკუმშულáƒáƒ‘ის ჭდის მáƒáƒ®áƒ¡áƒœáƒ/დáƒáƒ“ებრ(მხáƒáƒšáƒáƒ“ btrfs)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "ქვეტáƒáƒ›áƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ"
+
+#, fuzzy
+msgid "Partition"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ”ბი"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ მიმáƒáƒ’რების წერტილები შედáƒáƒ áƒ”ბითირდáƒáƒ§áƒ”ნების შიგნით. ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვáƒ, მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, /boot შეიძლებáƒ, იყáƒáƒ¡."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "შეიყვáƒáƒœáƒ”თ სáƒáƒ¬áƒ§áƒ˜áƒ¡áƒ˜ სექტáƒáƒ áƒ˜ (პრáƒáƒªáƒ”ნტებში áƒáƒœ ბლáƒáƒ™áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი. ნáƒáƒ’ულისხმები: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "შეიყვáƒáƒœáƒ”თ დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ ბáƒáƒšáƒ სექტáƒáƒ áƒ˜ (პრáƒáƒªáƒ”ნტულáƒáƒ“ áƒáƒœ ბლáƒáƒ™áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი. მáƒáƒ’: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
msgid "Encryption type"
msgstr "დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ˜áƒ¡ ტიპი"
@@ -856,23 +975,323 @@ msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ დისკის დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ˜áƒ¡ პ
msgid "Select a FIDO2 device to use for HSM"
msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ HSM-სთვის გáƒáƒ›áƒáƒ¡áƒáƒ§áƒ”ნებელი FIDO2 მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒ"
-msgid "All settings will be reset, are you sure?"
-msgstr "ყველრპáƒáƒ áƒáƒ›áƒ”ტრი დáƒáƒ‘რუნდებáƒ. დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული ბრძáƒáƒœáƒ“ებით?"
+#, fuzzy
+msgid "Use a best-effort default partition layout"
+msgstr "მáƒáƒœáƒ˜áƒ¨áƒœáƒ£áƒš დისკებზე ყველáƒáƒ¤áƒ áƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ დრდáƒáƒœáƒáƒ§áƒáƒ¤áƒ”ბის გáƒáƒœáƒšáƒáƒ’ების სáƒáƒ£áƒ™áƒ”თესრგáƒáƒœáƒšáƒáƒ’ების გáƒáƒ›áƒáƒ§áƒ”ნებáƒ"
-msgid "Back"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ”ბი"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "მáƒáƒ áƒ’ების გáƒáƒ áƒ”შე"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ"
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+#, fuzzy
+msgid "↠Back"
msgstr "უკáƒáƒœ"
msgid "Disk encryption"
msgstr "დისკის დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ"
+#, fuzzy
+msgid "Configuration"
+msgstr "მáƒáƒ áƒ’ების გáƒáƒ áƒ”შე"
+
msgid "Password"
msgstr "პáƒáƒ áƒáƒšáƒ˜"
-msgid "Partition encryption"
-msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ"
+msgid "All settings will be reset, are you sure?"
+msgstr "ყველრპáƒáƒ áƒáƒ›áƒ”ტრი დáƒáƒ‘რუნდებáƒ. დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული ბრძáƒáƒœáƒ“ებით?"
+
+msgid "Back"
+msgstr "უკáƒáƒœ"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "შეიყვáƒáƒœáƒ”თ სáƒáƒ¬áƒ§áƒ˜áƒ¡áƒ˜ სექტáƒáƒ áƒ˜ (პრáƒáƒªáƒ”ნტებში áƒáƒœ ბლáƒáƒ™áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი. ნáƒáƒ’ულისხმები: {}): "
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "შეიყვáƒáƒœáƒ”თ დáƒáƒœáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ ბáƒáƒšáƒ სექტáƒáƒ áƒ˜ (პრáƒáƒªáƒ”ნტულáƒáƒ“ áƒáƒœ ბლáƒáƒ™áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი. მáƒáƒ’: {}): "
+#, fuzzy
+msgid "Installed packages"
+msgstr "დáƒáƒ›áƒáƒ¢áƒ”ბითი პáƒáƒ™áƒ”ტები"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "პრáƒáƒ¤áƒ˜áƒšáƒ˜"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "პრáƒáƒ¤áƒ˜áƒšáƒ˜"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "ინტერფეისის წáƒáƒ¨áƒšáƒ"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "პრáƒáƒ¤áƒ˜áƒšáƒ˜"
+
+#, fuzzy
+msgid "The profile name you entered is already in use. Try again"
+msgstr "შეყვáƒáƒœáƒ˜áƒšáƒ˜ მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლის სáƒáƒ®áƒ”ლი áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ. კიდევ სცáƒáƒ“ეთ"
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "დáƒáƒ›áƒáƒ¢áƒ”ბითი პáƒáƒ™áƒ”ტები დáƒáƒ¡áƒáƒ§áƒ”ნებლáƒáƒ“ (გáƒáƒ›áƒáƒ¢áƒáƒ•áƒ”ბით გáƒáƒ›áƒáƒ§áƒáƒ¤áƒ˜áƒšáƒ˜, გáƒáƒ›áƒáƒ¡áƒáƒ¢áƒáƒ•áƒ”ბლáƒáƒ“ ცáƒáƒ áƒ˜áƒ”ლი დáƒáƒ¢áƒáƒ•áƒ”თ): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "დáƒáƒ›áƒáƒ¢áƒ”ბითი პáƒáƒ™áƒ”ტები დáƒáƒ¡áƒáƒ§áƒ”ნებლáƒáƒ“ (გáƒáƒ›áƒáƒ¢áƒáƒ•áƒ”ბით გáƒáƒ›áƒáƒ§áƒáƒ¤áƒ˜áƒšáƒ˜, გáƒáƒ›áƒáƒ¡áƒáƒ¢áƒáƒ•áƒ”ბლáƒáƒ“ ცáƒáƒ áƒ˜áƒ”ლი დáƒáƒ¢áƒáƒ•áƒ”თ): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ გრáƒáƒ¤áƒ˜áƒ™áƒ˜áƒ¡ დრáƒáƒ˜áƒ•áƒ”რი áƒáƒœ, ღირკáƒáƒ“ის მქáƒáƒœáƒ” დრáƒáƒ˜áƒ•áƒ”რის დáƒáƒ¡áƒáƒ§áƒ”ნებლáƒáƒ“, ცáƒáƒ áƒ˜áƒ”ლი დáƒáƒ¢áƒáƒ•áƒ”თ"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "მáƒáƒ áƒ’ების გáƒáƒ áƒ”შე"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "პრáƒáƒ¤áƒ˜áƒšáƒ˜"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ ერთი áƒáƒœ მეტი მყáƒáƒ áƒ˜ დისკი დრმáƒáƒ˜áƒ áƒ’ეთ"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "შეიყვáƒáƒœáƒ”თ მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლი სáƒáƒ®áƒ”ლი (გáƒáƒ›áƒáƒ¡áƒáƒ¢áƒáƒ•áƒ”ბლáƒáƒ“ ცáƒáƒ áƒ˜áƒ”ლი დáƒáƒ¢áƒáƒ•áƒ”თ): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "შეიყვáƒáƒœáƒ”თ მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლი სáƒáƒ®áƒ”ლი (გáƒáƒ›áƒáƒ¡áƒáƒ¢áƒáƒ•áƒ”ბლáƒáƒ“ ცáƒáƒ áƒ˜áƒ”ლი დáƒáƒ¢áƒáƒ•áƒ”თ): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ დისკის დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრი"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ დისკის დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრი"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლის კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡ შენáƒáƒ®áƒ•áƒ"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "შეიყვáƒáƒœáƒ”თ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე, სáƒáƒ“áƒáƒª კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜(ებ)-ი იქნებრშენáƒáƒ®áƒ£áƒšáƒ˜: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜ შენáƒáƒ®áƒ•áƒ"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "სáƒáƒ áƒ™áƒ˜áƒ¡ რეგიáƒáƒœáƒ˜"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "სáƒáƒ áƒ™áƒ˜áƒ¡ რეგიáƒáƒœáƒ˜"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - მინიმáƒáƒšáƒ£áƒ áƒ˜ მნიშვნელáƒáƒ‘რ: {} ( დáƒáƒ£áƒ¨áƒ•áƒ”ბს {} პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡, დáƒáƒ£áƒ¨áƒ•áƒ”ბს {} ერთდრáƒáƒ£áƒš გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "შეყვáƒáƒœáƒ˜áƒšáƒ˜ რიცხვი áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ! თáƒáƒ•áƒ˜áƒ“áƒáƒœ სცáƒáƒ“ეთ [1-დáƒáƒœ {}-მდე, áƒáƒœ 0, გáƒáƒ¡áƒáƒ—იშáƒáƒ“]"
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager-ის გáƒáƒ›áƒáƒ§áƒ”ნებრ(áƒáƒ£áƒªáƒ˜áƒšáƒ”ბელირინტერნეტის GNOME/KDE-დáƒáƒœ მáƒáƒ¡áƒáƒ áƒ’ებáƒáƒ“)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "შეიყვáƒáƒœáƒ”თ სáƒáƒ¬áƒ§áƒ˜áƒ¡áƒ˜ სექტáƒáƒ áƒ˜ (პრáƒáƒªáƒ”ნტებში áƒáƒœ ბლáƒáƒ™áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი. ნáƒáƒ’ულისხმები: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "შეიყვáƒáƒœáƒ”თ სáƒáƒ¬áƒ§áƒ˜áƒ¡áƒ˜ სექტáƒáƒ áƒ˜ (პრáƒáƒªáƒ”ნტებში áƒáƒœ ბლáƒáƒ™áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი. ნáƒáƒ’ულისხმები: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "ხელით მáƒáƒ áƒ’ებáƒ"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "ეს პáƒáƒ áƒáƒ›áƒ”ტრი დáƒáƒ§áƒ”ნებისáƒáƒ¡ მითითებული რáƒáƒáƒ“ენáƒáƒ‘ის პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ დáƒáƒ£áƒ¨áƒ•áƒ”ბს"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"შეიყვáƒáƒœáƒ”თ დáƒáƒ¡áƒáƒ¨áƒ•áƒ”ბი პáƒáƒ áƒáƒšáƒ”ლური გáƒáƒ“მáƒáƒ¬áƒ”რების რáƒáƒáƒ“ენáƒáƒ‘áƒ.\n"
+" (შეიყვáƒáƒœáƒ”თ მნიშვნელáƒáƒ‘რ1-დáƒáƒœ {}-მდე)\n"
+"დáƒáƒ˜áƒ›áƒáƒ®áƒ¡áƒáƒ•áƒ áƒ”თ:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - მინიმáƒáƒšáƒ£áƒ áƒ˜ მნიშვნელáƒáƒ‘რ: {} ( დáƒáƒ£áƒ¨áƒ•áƒ”ბს {} პáƒáƒ áƒáƒšáƒ”ლურ გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡, დáƒáƒ£áƒ¨áƒ•áƒ”ბს {} ერთდრáƒáƒ£áƒš გáƒáƒ“მáƒáƒ¬áƒ”რáƒáƒ¡ )"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - გáƒáƒ›áƒáƒ áƒ—ვáƒ/ნáƒáƒ’ულისხმები : 0 ( პáƒáƒ áƒáƒšáƒ”ლური გáƒáƒ“მáƒáƒ¬áƒ”რების გáƒáƒ—იშვáƒ. დრáƒáƒ˜áƒ¡ ერთ მáƒáƒ›áƒ”ნტში მხáƒáƒšáƒáƒ“ ერთი გáƒáƒ“მáƒáƒ¬áƒ”რრმáƒáƒ®áƒ“ებრ)"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "შეყვáƒáƒœáƒ˜áƒšáƒ˜ რიცხვი áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ! თáƒáƒ•áƒ˜áƒ“áƒáƒœ სცáƒáƒ“ეთ [1-დáƒáƒœ {}-მდე, áƒáƒœ 0, გáƒáƒ¡áƒáƒ—იშáƒáƒ“]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "გნებáƒáƒ•áƒ— სვáƒáƒžáƒ˜áƒ¡ ZRAM-ზე გáƒáƒ›áƒáƒ§áƒ”ნებáƒ?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "ინტერფეისის წáƒáƒ¨áƒšáƒ"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "დáƒáƒœáƒáƒ§áƒáƒ¤áƒ–ე შეკუმშულáƒáƒ‘ის ჭდის მáƒáƒ®áƒ¡áƒœáƒ/დáƒáƒ“ებრ(მხáƒáƒšáƒáƒ“ btrfs)"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "გნებáƒáƒ•áƒ— BTRFS-ის შეკუმშვის გáƒáƒ›áƒáƒ§áƒ”ნებáƒ?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/ko/LC_MESSAGES/base.mo b/archinstall/locales/ko/LC_MESSAGES/base.mo
index 4c89f1f8..098713fa 100644
--- a/archinstall/locales/ko/LC_MESSAGES/base.mo
+++ b/archinstall/locales/ko/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po
index 4363031e..e18dee9f 100644
--- a/archinstall/locales/ko/LC_MESSAGES/base.po
+++ b/archinstall/locales/ko/LC_MESSAGES/base.po
@@ -62,7 +62,7 @@ msgstr "설치할 추가 패키지 작성하세요 (ë„어쓰기로 구분, ê±´ë
msgid "Copy ISO network configuration to installation"
msgstr "ISO ë„¤íŠ¸ì›Œí¬ êµ¬ì„±ì„ ì„¤ì¹˜ì— ë³µì‚¬"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "NetworkManager 사용 (GNOME ì´ë‚˜ KDE ì—ì„œ 그래픽으로 ì¸í„°ë„·ì„ 구성하는 ë° í•„ìš”)"
msgid "Select one network interface to configure"
@@ -794,18 +794,17 @@ msgstr "êµ¬ì„±ëœ {} ì¸í„°íŽ˜ì´ìŠ¤"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "ì´ ì˜µì…˜ì€ ì„¤ì¹˜ ì¤‘ì— ë°œìƒí•  수 있는 병렬 다운로드 수를 활성화합니다"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"활성화할 병렬 다운로드 수를 입력하세요.\n"
-" (1 부터 {max_downloads} ê¹Œì§€ì˜ ìˆ«ìžì¤‘ 하나를 입력하세요)\n"
+" (1 부터 {} ê¹Œì§€ì˜ ìˆ«ìžì¤‘ 하나를 입력하세요)\n"
"메모:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - 최댓값 : {max_downloads} ( {max_downloads} ê°œì˜ ë³‘ë ¬ 다운로드 허용, í•œ ë²ˆì— {max_downloads+1} ê°œì˜ ë‹¤ìš´ë¡œë“œ 허용 )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - 최댓값 : {} ( {} ê°œì˜ ë³‘ë ¬ 다운로드 허용, í•œ ë²ˆì— {} ê°œì˜ ë‹¤ìš´ë¡œë“œ 허용 )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - 최솟값 : 1 ( 1 ê°œì˜ ë³‘ë ¬ 다운로드 허용, í•œ ë²ˆì— 2 ê°œì˜ ë‹¤ìš´ë¡œë“œ 허용 )"
@@ -813,9 +812,9 @@ msgstr " - 최솟값 : 1 ( 1 ê°œì˜ ë³‘ë ¬ 다운로드 허용, í•œ ë²ˆì— 2 ê
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
msgstr " - 비활성화/기본 : - ( 병렬 다운로드 비활성화, í•œ ë²ˆì— 1 ê°œì˜ ë‹¤ìš´ë¡œë“œë§Œ 허용 )"
-#, python-brace-format
+#, fuzzy, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "ìž˜ëª»ëœ ê°’ìž…ë‹ˆë‹¤! 유효한 값으로 다시 ì‹œë„해주세요 [1 부터 {max_downloads} 까지, 비활성화 하려면 0]"
+msgstr "ìž˜ëª»ëœ ê°’ìž…ë‹ˆë‹¤! 유효한 값으로 다시 ì‹œë„해주세요 [1 부터 {} 까지, 비활성화 하려면 0]"
msgid "Parallel Downloads"
msgstr "병렬 다운로드"
@@ -838,6 +837,125 @@ msgstr "ì´ ë²ˆì—­ì„ ì‚¬ìš©í•˜ë ¤ë©´ 해당 언어를 지ì›í•˜ëŠ” ê¸€ê¼´ì„ ìˆ
msgid "The font should be stored as {}"
msgstr "ê¸€ê¼´ì€ {} (으)ë¡œ 저장해야 합니다"
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "'{}' ì— ëŒ€í•œ ìž‘ì—… ì„ íƒ"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "사용하고 구성할 하드 ë“œë¼ì´ë¸Œë¥¼ 하나 ì´ìƒ ì„ íƒí•˜ì„¸ìš”"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "하드 ë“œë¼ì´ë¸Œ ì„ íƒì„ 재설정하면 현재 ë””ìŠ¤í¬ ë ˆì´ì•„ì›ƒë„ ìž¬ì„¤ì •ë©ë‹ˆë‹¤. ì •ë§ ì§„í–‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "파티션 추가 중...."
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "파티션 제거"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "저장할 êµ¬ì„±ì˜ ë””ë ‰í† ë¦¬ë¥¼ 입력하세요: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Arch Linux íŒŒí‹°ì…˜ì˜ ìµœëŒ€ 용량: {}GB\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Arch Linux íŒŒí‹°ì…˜ì˜ ìµœì†Œ 용량: {}GB"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "ì´ê²ƒì€ 사전 프로그래ë°ëœ 프로필 목ë¡ì´ë©° ë°ìŠ¤í¬í†± 환경과 ê°™ì€ ê²ƒì„ ë” ì‰½ê²Œ 설치할 수 있습니다"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "현재 파티션 ë ˆì´ì•„웃"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "새 파티션 ìƒì„±"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "íŒŒí‹°ì…˜ì— ëŒ€í•œ 마운트 ì§€ì  í• ë‹¹"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "í¬ë§·í•  파티션 표시/표시 í•´ì œ (ë°ì´í„° ì‚­ì œ)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "íŒŒí‹°ì…˜ì„ ì••ì¶•ëœ ê²ƒìœ¼ë¡œ 표시/표시 í•´ì œ(btrfs만 해당)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "하위 볼륨 제거"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "파티션 제거"
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * 파티션 마운트 í¬ì¸íŠ¸ëŠ” 설치 내부를 기준으로 하며 예를 들어 ë¶€íŒ…ì€ /boot 입니다."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "올바른 디렉터리가 아닙니다: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "시작 섹터를 입력하세요 (백분율 ë˜ëŠ” ë¸”ë¡ ë²ˆí˜¸, 기본값: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "íŒŒí‹°ì…˜ì˜ ë 섹터를 입력하세요 (백분율 ë˜ëŠ” ë¸”ë¡ ë²ˆí˜¸, 예시: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
#, fuzzy
msgid "Encryption type"
msgstr "비밀번호 암호화"
@@ -859,24 +977,323 @@ msgid "Select a FIDO2 device to use for HSM"
msgstr ""
#, fuzzy
-msgid "All settings will be reset, are you sure?"
-msgstr "{} ì— ëŒ€ê¸° ì¤‘ì¸ íŒŒí‹°ì…˜ì´ í¬í•¨ë˜ì–´ 있습니다. 그러면 ì´ëŸ¬í•œ íŒŒí‹°ì…˜ì´ ì œê±°ë©ë‹ˆë‹¤. ì •ë§ ì§„í–‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+msgid "Use a best-effort default partition layout"
+msgstr "ì„ íƒí•œ 모든 ë“œë¼ì´ë¸Œë¥¼ 지우고 ìµœì„ ì˜ ê¸°ë³¸ 파티션 ë ˆì´ì•„웃 사용"
-msgid "Back"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "ìˆ˜ë™ êµ¬ì„±"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "구성 ì—†ìŒ"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
#, fuzzy
+msgid "Configuration"
+msgstr "구성 ì—†ìŒ"
+
+#, fuzzy
msgid "Password"
msgstr "루트 비밀번호"
-msgid "Partition encryption"
+#, fuzzy
+msgid "All settings will be reset, are you sure?"
+msgstr "{} ì— ëŒ€ê¸° ì¤‘ì¸ íŒŒí‹°ì…˜ì´ í¬í•¨ë˜ì–´ 있습니다. 그러면 ì´ëŸ¬í•œ íŒŒí‹°ì…˜ì´ ì œê±°ë©ë‹ˆë‹¤. ì •ë§ ì§„í–‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+#, fuzzy
+msgid "Installed packages"
+msgstr "추가 패키지"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "프로필"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "프로필"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "ì¸í„°íŽ˜ì´ìŠ¤ 제거"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "프로필"
+
+#, fuzzy
+msgid "The profile name you entered is already in use. Try again"
+msgstr "입력한 ì‚¬ìš©ìž ì´ë¦„ì´ ìž˜ëª»ë˜ì—ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„하세요"
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "설치할 추가 패키지 작성하세요 (ë„어쓰기로 구분, 건너뛰려면 공백): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "설치할 추가 패키지 작성하세요 (ë„어쓰기로 구분, 건너뛰려면 공백): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"모든 오픈 소스 ë“œë¼ì´ë²„를 설치하려면 그래픽 ë“œë¼ì´ë²„를 ì„ íƒí•˜ê±°ë‚˜ 공백으로 ë‘세요"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "구성 ì—†ìŒ"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "프로필"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "사용하고 구성할 하드 ë“œë¼ì´ë¸Œë¥¼ 하나 ì´ìƒ ì„ íƒí•˜ì„¸ìš”"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "ì‚¬ìš©ìž ì¶”ê°€"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "사용ìžëª… ìž…ë ¥ (ê³µë°±ì¼ ê²½ìš° 스킵): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "사용ìžëª… ìž…ë ¥ (ê³µë°±ì¼ ê²½ìš° 스킵): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "추가할 ì¸í„°íŽ˜ì´ìŠ¤ ì„ íƒ"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "추가할 ì¸í„°íŽ˜ì´ìŠ¤ ì„ íƒ"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "ì‚¬ìš©ìž êµ¬ì„± 저장"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "저장할 êµ¬ì„±ì˜ ë””ë ‰í† ë¦¬ë¥¼ 입력하세요: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "구성 저장"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "미러 위치"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "미러 위치"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - 최댓값 : {} ( {} ê°œì˜ ë³‘ë ¬ 다운로드 허용, í•œ ë²ˆì— {} ê°œì˜ ë‹¤ìš´ë¡œë“œ 허용 )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "ìž˜ëª»ëœ ê°’ìž…ë‹ˆë‹¤! 유효한 값으로 다시 ì‹œë„해주세요 [1 부터 {} 까지, 비활성화 하려면 0]"
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager 사용 (GNOME ì´ë‚˜ KDE ì—ì„œ 그래픽으로 ì¸í„°ë„·ì„ 구성하는 ë° í•„ìš”)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "시작 섹터를 입력하세요 (백분율 ë˜ëŠ” ë¸”ë¡ ë²ˆí˜¸, 기본값: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "시작 섹터를 입력하세요 (백분율 ë˜ëŠ” ë¸”ë¡ ë²ˆí˜¸, 기본값: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "시작 섹터를 입력하세요 (백분율 ë˜ëŠ” ë¸”ë¡ ë²ˆí˜¸, 기본값: {}): "
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "ìˆ˜ë™ êµ¬ì„±"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "ì´ ì˜µì…˜ì€ ì„¤ì¹˜ ì¤‘ì— ë°œìƒí•  수 있는 병렬 다운로드 수를 활성화합니다"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"활성화할 병렬 다운로드 수를 입력하세요.\n"
+" (1 부터 {} ê¹Œì§€ì˜ ìˆ«ìžì¤‘ 하나를 입력하세요)\n"
+"메모:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - 최댓값 : {} ( {} ê°œì˜ ë³‘ë ¬ 다운로드 허용, í•œ ë²ˆì— {} ê°œì˜ ë‹¤ìš´ë¡œë“œ 허용 )"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - 비활성화/기본 : - ( 병렬 다운로드 비활성화, í•œ ë²ˆì— 1 ê°œì˜ ë‹¤ìš´ë¡œë“œë§Œ 허용 )"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "íŒŒí‹°ì…˜ì˜ ë 섹터를 입력하세요 (백분율 ë˜ëŠ” ë¸”ë¡ ë²ˆí˜¸, 예시: {}): "
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "ìž˜ëª»ëœ ê°’ìž…ë‹ˆë‹¤! 유효한 값으로 다시 ì‹œë„해주세요 [1 부터 {} 까지, 비활성화 하려면 0]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "zramì—ì„œ ìŠ¤ì™‘ì„ ì‚¬ìš©í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "ì¸í„°íŽ˜ì´ìŠ¤ 제거"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "íŒŒí‹°ì…˜ì„ ì••ì¶•ëœ ê²ƒìœ¼ë¡œ 표시/표시 í•´ì œ(btrfs만 해당)"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "BTRFS ì••ì¶•ì„ ì‚¬ìš©í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json
index 1e33dcde..915a6c5e 100644
--- a/archinstall/locales/languages.json
+++ b/archinstall/locales/languages.json
@@ -36,7 +36,7 @@
{"abbr": "el", "lang": "Modern Greek (1453-)", "translated_lang": "Ελληνικά"},
{"abbr": "en", "lang": "English"},
{"abbr": "eo", "lang": "Esperanto"},
- {"abbr": "et", "lang": "Estonian"},
+ {"abbr": "et", "lang": "Estonian", "translated_lang": "Eesti" },
{"abbr": "eu", "lang": "Basque"},
{"abbr": "ee", "lang": "Ewe"},
{"abbr": "fo", "lang": "Faroese"},
@@ -94,7 +94,7 @@
{"abbr": "lv", "lang": "Latvian"},
{"abbr": "li", "lang": "Limburgan"},
{"abbr": "ln", "lang": "Lingala"},
- {"abbr": "lt", "lang": "Lithuanian"},
+ {"abbr": "lt", "lang": "Lithuanian", "translated_lang": "Lietuvių"},
{"abbr": "lb", "lang": "Luxembourgish"},
{"abbr": "lu", "lang": "Luba-Katanga"},
{"abbr": "lg", "lang": "Ganda"},
@@ -132,7 +132,7 @@
{"abbr": "ps", "lang": "Pushto"},
{"abbr": "qu", "lang": "Quechua"},
{"abbr": "rm", "lang": "Romansh"},
- {"abbr": "ro", "lang": "Romanian"},
+ {"abbr": "ro", "lang": "Romanian", "translated_lang": "Română"},
{"abbr": "rn", "lang": "Rundi"},
{"abbr": "ru", "lang": "Russian", "translated_lang": "РуÑÑкий"},
{"abbr": "sg", "lang": "Sango"},
@@ -169,7 +169,7 @@
{"abbr": "tr", "lang": "Turkish", "translated_lang" : "Türkçe"},
{"abbr": "tw", "lang": "Twi"},
{"abbr": "ug", "lang": "Uighur"},
- {"abbr": "uk", "lang": "Ukrainian", "translated_lang": "УкраїнÑька"},
+ {"abbr": "uk", "lang": "Ukrainian"},
{"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو"},
{"abbr": "uz", "lang": "Uzbek"},
{"abbr": "ve", "lang": "Venda"},
@@ -181,6 +181,6 @@
{"abbr": "yi", "lang": "Yiddish"},
{"abbr": "yo", "lang": "Yoruba"},
{"abbr": "za", "lang": "Zhuang"},
- {"abbr": "zh-TW", "lang": "Traditional Chinese"},
- {"abbr": "zh-CN", "lang": "Simplified Chinese"},
+ {"abbr": "zh-CN", "lang": "Simplified Chinese", "translated_lang": "简体中文"},
+ {"abbr": "zh-TW", "lang": "Traditional Chinese", "translated_lang": "ç¹é«”中文"},
{"abbr": "zu", "lang": "Zulu"}]
diff --git a/archinstall/locales/locales_generator.sh b/archinstall/locales/locales_generator.sh
index cdd5be31..80e3bd03 100755
--- a/archinstall/locales/locales_generator.sh
+++ b/archinstall/locales/locales_generator.sh
@@ -1,12 +1,48 @@
-#!/bin/bash
+#!/usr/bin/env bash
+set -euo pipefail
cd $(dirname "$0")/..
+function update_lang() {
+ file=${1}
+
+ echo "Updating: ${file}"
+ path=$(dirname "${file}")
+ msgmerge --quiet --no-location --width 512 --backup none --update "${file}" locales/base.pot
+ msgfmt -o "${path}/base.mo" "${file}"
+}
+
+
+function generate_all() {
+ for file in $(find locales/ -name "base.po"); do
+ update_lang "${file}"
+ done
+}
+
+function generate_single_lang() {
+ lang_file="locales/${1}/LC_MESSAGES/base.po"
+
+ if [ ! -f "${lang_file}" ]; then
+ echo "Language files not found: ${lang_file}"
+ exit 1
+ fi
+
+ update_lang "${lang_file}"
+}
+
+
+if [ $# -eq 0 ]; then
+ echo "Usage: ${0} <language_abbr>"
+ echo "Special case 'all' for <language_abbr> builds all languages."
+ exit 1
+fi
+
+lang=${1}
+
+# Update the base file containing all translatable strings
find . -type f -iname "*.py" | xargs xgettext --join-existing --no-location --omit-header -d base -o locales/base.pot
-for file in $(find locales/ -name "base.po"); do
- echo "Updating: $file"
- path=$(dirname $file)
- msgmerge --quiet --no-location --width 512 --backup none --update $file locales/base.pot
- msgfmt -o $path/base.mo $file
-done
+case "${lang}" in
+ "all") generate_all;;
+ *) generate_single_lang "${lang}"
+esac
diff --git a/archinstall/locales/lt/LC_MESSAGES/base.mo b/archinstall/locales/lt/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..a98027c6
--- /dev/null
+++ b/archinstall/locales/lt/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/lt/LC_MESSAGES/base.po b/archinstall/locales/lt/LC_MESSAGES/base.po
new file mode 100644
index 00000000..2a9174c9
--- /dev/null
+++ b/archinstall/locales/lt/LC_MESSAGES/base.po
@@ -0,0 +1,1181 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Florijan Demidov https://github.com/FlorijanDem \n"
+"Language-Team: \n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.3\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] Žurnalo failas įraÅ¡ytas Äia: {} {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " Prašome pateiktį šitą problemą (ir failą) https://github.com/archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "Ar jus noritÄ— nutraukti?"
+
+msgid "And one more time for verification: "
+msgstr ""
+
+msgid "Would you like to use swap on zram?"
+msgstr ""
+
+msgid "Desired hostname for the installation: "
+msgstr ""
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr ""
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr ""
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "Ar noritÄ— padarity Å¡itÄ… vartotojÄ… supervartotoju (sudoer)"
+
+msgid "Select a timezone"
+msgstr "IÅ¡rinkite laiko zona"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "Ar jus norÄ—tum naudoti GRUB paleidyklÄ—, o nÄ— systemd-boot?"
+
+msgid "Choose a bootloader"
+msgstr "IÅ¡rinkite paleidyklÄ—"
+
+msgid "Choose an audio server"
+msgstr "Išrinkite garso serverį"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "Tiktai paketai tokie kaip: base, base-devel, linux, linux-firmware, efibootmgr ir neprivalomi paketai bus įdegtį."
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr ""
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Copy ISO network configuration to installation"
+msgstr ""
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr ""
+
+msgid "Select one network interface to configure"
+msgstr ""
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr ""
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr ""
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr ""
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr ""
+
+msgid "Select which filesystem your main partition should use"
+msgstr ""
+
+msgid "Current partition layout"
+msgstr ""
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr ""
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr ""
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr ""
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr ""
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr ""
+
+msgid "Archinstall language"
+msgstr "Archinstall liežuvis"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr ""
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr ""
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr ""
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr ""
+
+msgid "Select keyboard layout"
+msgstr "Išrinkite klaviatūros išdėstyma"
+
+msgid "Select one of the regions to download packages from"
+msgstr ""
+
+msgid "Select one or more hard drives to use and configure"
+msgstr ""
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr ""
+
+msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
+msgstr ""
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid "All open-source (default)"
+msgstr ""
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr ""
+
+msgid "Choose which locale language to use"
+msgstr ""
+
+msgid "Choose which locale encoding to use"
+msgstr ""
+
+msgid "Select one of the values shown below: "
+msgstr ""
+
+msgid "Select one or more of the options below: "
+msgstr ""
+
+msgid "Adding partition...."
+msgstr ""
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr ""
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr ""
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr ""
+
+msgid "Keyboard layout"
+msgstr ""
+
+msgid "Mirror region"
+msgstr ""
+
+msgid "Locale language"
+msgstr "Lokalės liežuvis"
+
+msgid "Locale encoding"
+msgstr "LokalÄ—s koduotÄ—"
+
+msgid "Drive(s)"
+msgstr ""
+
+msgid "Disk layout"
+msgstr ""
+
+msgid "Encryption password"
+msgstr ""
+
+msgid "Swap"
+msgstr ""
+
+msgid "Bootloader"
+msgstr "PaleidyklÄ—"
+
+msgid "Root password"
+msgstr "Root Slaptažodis"
+
+msgid "Superuser account"
+msgstr "Supervartotojo paskirÄ…"
+
+msgid "User account"
+msgstr "Vartotojo paskirÄ…"
+
+msgid "Profile"
+msgstr "Profilis"
+
+msgid "Audio"
+msgstr "Garsas"
+
+msgid "Kernels"
+msgstr "Branduoliai"
+
+msgid "Additional packages"
+msgstr ""
+
+msgid "Network configuration"
+msgstr "Tinklo konfigūraciją"
+
+msgid "Automatic time sync (NTP)"
+msgstr "AutomatinÄ— laiko sinchonizacijÄ…"
+
+msgid "Install ({} config(s) missing)"
+msgstr "Įdiegti ({} config(s) missing)"
+
+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 ""
+
+msgid "Re-using partition instance: {}"
+msgstr ""
+
+msgid "Create a new partition"
+msgstr "Padariti naują skaidinį"
+
+msgid "Delete a partition"
+msgstr "Šalinti skaidinį"
+
+msgid "Clear/Delete all partitions"
+msgstr "IÅ¡trinti/Å alinti visus skaidinius"
+
+msgid "Assign mount-point for a partition"
+msgstr ""
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr ""
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr ""
+
+msgid "Set desired filesystem for a partition"
+msgstr ""
+
+msgid "Abort"
+msgstr "Nutraukti"
+
+msgid "Hostname"
+msgstr "Pagrindinio kompiuterio vardas"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr ""
+
+msgid "Timezone"
+msgstr "Laiko zona"
+
+msgid "Set/Modify the below options"
+msgstr ""
+
+msgid "Install"
+msgstr "Įdiegti"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr "Naudokit ESC tam, kad praleistį\n"
+
+msgid "Suggest partition layout"
+msgstr ""
+
+msgid "Enter a password: "
+msgstr "Iveskite slaptažodžį"
+
+msgid "Enter a encryption password for {}"
+msgstr ""
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr ""
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr ""
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr ""
+
+msgid "Password for user \"{}\": "
+msgstr "Slaptažodis vartotojo \"{}\": "
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr ""
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr ""
+
+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 ""
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr ""
+
+msgid "Use ESC to skip\n"
+msgstr "Naudokit ESC tam, kad praleistį\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Confirm and exit"
+msgstr ""
+
+msgid "Add"
+msgstr "PridÄ—ti"
+
+msgid "Copy"
+msgstr "Kopijuoti"
+
+msgid "Edit"
+msgstr "Redaguoti"
+
+msgid "Delete"
+msgstr "Pašalinti"
+
+msgid "Select an action for '{}'"
+msgstr ""
+
+msgid "Copy to new key:"
+msgstr ""
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr ""
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr ""
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr ""
+
+msgid "Choose which optional additional repositories to enable"
+msgstr ""
+
+msgid "Add a user"
+msgstr "PridÄ—ti vartotojÄ…"
+
+msgid "Change password"
+msgstr "Pakeisti slaptažodžį"
+
+msgid "Promote/Demote user"
+msgstr ""
+
+msgid "Delete User"
+msgstr "Pašalinti vartotoją"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+
+msgid "User Name : "
+msgstr "Vartotojo vardas :"
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr ""
+
+msgid "Define users with sudo privilege: "
+msgstr ""
+
+msgid "No network configuration"
+msgstr ""
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr ""
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr ""
+
+msgid "No configuration"
+msgstr "Nėra konfigūracijos"
+
+msgid "Save user configuration"
+msgstr "Išsaugoti vartotojo konfigūraciją"
+
+msgid "Save user credentials"
+msgstr ""
+
+msgid "Save disk layout"
+msgstr ""
+
+msgid "Save all"
+msgstr "IÅ¡saugoti viskÄ…"
+
+msgid "Choose which configuration to save"
+msgstr ""
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr ""
+
+msgid "Not a valid directory: {}"
+msgstr ""
+
+msgid "The password you are using seems to be weak,"
+msgstr ""
+
+msgid "are you sure you want to use it?"
+msgstr ""
+
+msgid "Optional repositories"
+msgstr ""
+
+msgid "Save configuration"
+msgstr "Išsaugoti konfigūraciją"
+
+msgid "Missing configurations:\n"
+msgstr ""
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr ""
+
+msgid "Manage superuser accounts: "
+msgstr ""
+
+msgid "Manage ordinary user accounts: "
+msgstr ""
+
+msgid " Subvolume :{:16}"
+msgstr ""
+
+msgid " mounted at {:16}"
+msgstr ""
+
+msgid " with option {}"
+msgstr ""
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+
+msgid "Subvolume name "
+msgstr ""
+
+msgid "Subvolume mountpoint"
+msgstr ""
+
+msgid "Subvolume options"
+msgstr ""
+
+msgid "Save"
+msgstr "IÅ¡saugoti"
+
+msgid "Subvolume name :"
+msgstr ""
+
+msgid "Select a mount point :"
+msgstr ""
+
+msgid "Select the desired subvolume options "
+msgstr ""
+
+msgid "Define users with sudo privilege, by username: "
+msgstr ""
+
+msgid "[!] A log file has been created here: {}"
+msgstr ""
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr ""
+
+msgid "Would you like to use BTRFS compression?"
+msgstr ""
+
+msgid "Would you like to create a separate partition for /home?"
+msgstr ""
+
+msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr ""
+
+msgid "Continue"
+msgstr "Tęsti"
+
+msgid "yes"
+msgstr "taip"
+
+msgid "no"
+msgstr "nÄ—"
+
+msgid "set: {}"
+msgstr ""
+
+msgid "Manual configuration setting must be a list"
+msgstr ""
+
+msgid "No iface specified for manual configuration"
+msgstr ""
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr ""
+
+msgid "Add interface"
+msgstr ""
+
+msgid "Edit interface"
+msgstr ""
+
+msgid "Delete interface"
+msgstr ""
+
+msgid "Select interface to add"
+msgstr ""
+
+msgid "Manual configuration"
+msgstr ""
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr ""
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr ""
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr ""
+
+msgid "Select your desired desktop environment"
+msgstr ""
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr ""
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr ""
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr ""
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr ""
+
+msgid "Press Enter to continue."
+msgstr "Paspauskite Enter tam, kad tęstį."
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr ""
+
+msgid "Are you sure you want to reset this setting?"
+msgstr ""
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr ""
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr ""
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+msgid "Save and exit"
+msgstr ""
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+
+msgid "No audio server"
+msgstr "BÄ— garso serverio"
+
+msgid "(default)"
+msgstr ""
+
+msgid "Use ESC to skip"
+msgstr "Naudokit ESC tam, kad praleistį"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+
+msgid "Copy to: "
+msgstr "Kopijuoti į: "
+
+msgid "Edit: "
+msgstr "Redaguoti: "
+
+msgid "Key: "
+msgstr "Raktas: "
+
+msgid "Edit {}: "
+msgstr "Redaguoti {}: "
+
+msgid "Add: "
+msgstr "PridÄ—ti: "
+
+msgid "Value: "
+msgstr "Reikšmė: "
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr ""
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr ""
+
+msgid "Select which partitions to mark for formatting:"
+msgstr ""
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr ""
+
+msgid "Device"
+msgstr "Įrenginys"
+
+msgid "Size"
+msgstr "Didis"
+
+msgid "Free space"
+msgstr "Laisva vietÄ…"
+
+msgid "Bus-type"
+msgstr ""
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr ""
+
+msgid "Enter username (leave blank to skip): "
+msgstr ""
+
+msgid "The username you entered is invalid. Try again"
+msgstr ""
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr ""
+
+msgid "Select which partitions to encrypt"
+msgstr ""
+
+msgid "very weak"
+msgstr "labai silpnas"
+
+msgid "weak"
+msgstr "silpnas"
+
+msgid "moderate"
+msgstr "vidutinis"
+
+msgid "strong"
+msgstr "geras"
+
+msgid "Add subvolume"
+msgstr ""
+
+msgid "Edit subvolume"
+msgstr ""
+
+msgid "Delete subvolume"
+msgstr ""
+
+msgid "Configured {} interfaces"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr ""
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr ""
+
+msgid "Parallel Downloads"
+msgstr ""
+
+msgid "ESC to skip"
+msgstr "ESC tam, kad praleistį"
+
+msgid "CTRL+C to reset"
+msgstr ""
+
+msgid "TAB to select"
+msgstr "TAB tam, kad išrinktį"
+
+msgid "[Default value: 0] > "
+msgstr ""
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr ""
+
+msgid "The font should be stored as {}"
+msgstr ""
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+msgid "Select an execution mode"
+msgstr ""
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+msgid "Select one or more devices to use and configure"
+msgstr ""
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+msgid "Existing Partitions"
+msgstr ""
+
+msgid "Select a partitioning option"
+msgstr ""
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr ""
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr ""
+
+msgid "Current profile selection"
+msgstr ""
+
+msgid "Remove all newly added partitions"
+msgstr ""
+
+msgid "Assign mountpoint"
+msgstr ""
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr ""
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+msgid "Mark/Unmark as compressed"
+msgstr ""
+
+msgid "Set subvolumes"
+msgstr ""
+
+msgid "Delete partition"
+msgstr "Pašalinti skaidynį"
+
+msgid "Partition"
+msgstr "Skaidinys"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr ""
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+msgid "Total sectors: {}"
+msgstr ""
+
+msgid "Enter the start sector (default: {}): "
+msgstr ""
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr ""
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
+msgid "Encryption type"
+msgstr ""
+
+msgid "Partitions"
+msgstr "Skaidiniai"
+
+msgid "No HSM devices available"
+msgstr ""
+
+msgid "Partitions to be encrypted"
+msgstr ""
+
+msgid "Select disk encryption option"
+msgstr ""
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr ""
+
+msgid "Use a best-effort default partition layout"
+msgstr ""
+
+msgid "Manual Partitioning"
+msgstr ""
+
+msgid "Pre-mounted configuration"
+msgstr ""
+
+msgid "Unknown"
+msgstr "Nežinomas"
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
+msgstr "↠Atgal"
+
+msgid "Disk encryption"
+msgstr ""
+
+msgid "Configuration"
+msgstr ""
+
+msgid "Password"
+msgstr "Slaptažodis"
+
+msgid "All settings will be reset, are you sure?"
+msgstr ""
+
+msgid "Back"
+msgstr "Atgal"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+msgid "Installed packages"
+msgstr "Įdiegti paketus"
+
+msgid "Add profile"
+msgstr "Pridėti profilį"
+
+msgid "Edit profile"
+msgstr "Redaguoti profilį"
+
+msgid "Delete profile"
+msgstr "Pašalinti profilį"
+
+msgid "Profile name: "
+msgstr "Profilio vardas: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr ""
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr "Vaizdo plokštės draiveris"
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+msgid "Disk configuration"
+msgstr "Disko konfigūraciją"
+
+msgid "Profiles"
+msgstr ""
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr ""
+
+msgid "Add a custom mirror"
+msgstr "PridÄ—ti savo 'custom mirror'"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+msgid "Enter name (leave blank to skip): "
+msgstr ""
+
+msgid "Enter url (leave blank to skip): "
+msgstr ""
+
+msgid "Select signature check option"
+msgstr ""
+
+msgid "Select signature option"
+msgstr ""
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+msgid "Save user configuration (including disk layout)"
+msgstr ""
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+msgid "Saving {} configuration files to {}"
+msgstr ""
+
+msgid "Mirrors"
+msgstr ""
+
+msgid "Mirror regions"
+msgstr ""
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr "LokalÄ—s"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr ""
+
+msgid "Total: {} / {}"
+msgstr "IÅ¡ viso: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+msgid "Enter start (default: sector {}): "
+msgstr ""
+
+msgid "Enter end (default: {}): "
+msgstr ""
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr ""
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "Would you like to use unified kernel images?"
+msgstr ""
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Pašalinti profilį"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Mark/Unmark as nodatacow"
+msgstr ""
+
+msgid "Would you like to use compression or disable CoW?"
+msgstr ""
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/nl/LC_MESSAGES/base.mo b/archinstall/locales/nl/LC_MESSAGES/base.mo
index 222f21e9..6f33f8ba 100644
--- a/archinstall/locales/nl/LC_MESSAGES/base.mo
+++ b/archinstall/locales/nl/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po
index 7f3de195..b10f9937 100644
--- a/archinstall/locales/nl/LC_MESSAGES/base.po
+++ b/archinstall/locales/nl/LC_MESSAGES/base.po
@@ -63,7 +63,7 @@ msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg
msgid "Copy ISO network configuration to installation"
msgstr "ISO-netwerkinstellingen overzetten naar fysieke installatie"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "NetworkManager gebruiken (benodigd om internetinstellingen grafisch in te stellen in GNOME en KDE)"
msgid "Select one network interface to configure"
@@ -823,14 +823,13 @@ msgstr ""
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr ""
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
msgstr ""
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
@@ -865,6 +864,122 @@ msgstr ""
msgid "The font should be stored as {}"
msgstr ""
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "Kies een actie voor '{}'"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "Selecteer één of meer in te stellen harde schijven"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "Bezig met toevoegen van partitie…"
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "Partitie verwijderen"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Voer de naam in van de map waarin de configuratie(s) moet(en) worden vastgelegd: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Dit is een vooraf opgestelde lijst met profielen, welke het installeren van zaken als werkomgevingen vereenvoudigt"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "Huidige partitie-indeling"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "Partitie aanmaken"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "Aankoppelpunt toekennen aan partitie"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Partitie (de)markeren voor formatteren (alle gegevens worden gewist)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "Partitie (de)markeren voor versleuteling"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "Gebruiker verwijderen"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "Partitie verwijderen"
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " *Partitie-aankoppelpunten zijn gekoppeld aan de fysieke installatie. Voorbeeld: ‘boot’ wordt ‘/boot’."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "Ongeldige map: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Voer de eindsector in (percentage of bloknummer - bijvoorbeeld: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
#, fuzzy
msgid "Encryption type"
msgstr "Versleutelwachtwoord instellen"
@@ -887,27 +1002,316 @@ msgid "Select a FIDO2 device to use for HSM"
msgstr ""
#, fuzzy
-msgid "All settings will be reset, are you sure?"
-msgstr "‘{}’ bevat in behandeling zijnde partities, welke hierdoor worden verwijderd. Weet u zeker dat u wilt doorgaan?"
+msgid "Use a best-effort default partition layout"
+msgstr "Alle geselecteerde schijven formatteren en best mogelijke partitie-indeling gebruiken"
-msgid "Back"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "Configuratie vastleggen"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "Geen configuratie"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
#, fuzzy
+msgid "Configuration"
+msgstr "Geen configuratie"
+
+#, fuzzy
msgid "Password"
msgstr "Rootwachtwoord"
-msgid "Partition encryption"
+#, fuzzy
+msgid "All settings will be reset, are you sure?"
+msgstr "‘{}’ bevat in behandeling zijnde partities, welke hierdoor worden verwijderd. Weet u zeker dat u wilt doorgaan?"
+
+msgid "Back"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): "
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Voer de eindsector in (percentage of bloknummer - bijvoorbeeld: {}): "
+#, fuzzy
+msgid "Installed packages"
+msgstr "Aanvullende pakketten"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "Profiel"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "Profiel"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "Gebruiker verwijderen"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "Profiel"
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg om over te slaan): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg om over te slaan): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Kies een grafisch stuurprogramma of laat leeg om alle opensource-stuurprogramma's te installeren"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "Geen configuratie"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "Profiel"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Selecteer één of meer in te stellen harde schijven"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "Gebruiker toevoegen"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "Voer een gebruikersnaam in om een tweede account toe te voegen (laat leeg om over te slaan): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "Voer een gebruikersnaam in om een tweede account toe te voegen (laat leeg om over te slaan): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "Kies een schijfindeling"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "Kies een schijfindeling"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "Gebruikersconfiguratie vastleggen"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "Voer de naam in van de map waarin de configuratie(s) moet(en) worden vastgelegd: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "Configuratie vastleggen"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "Spiegelserverregio"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "Spiegelserverregio"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager gebruiken (benodigd om internetinstellingen grafisch in te stellen in GNOME en KDE)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configuratie vastleggen"
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Wilt u wisselgeheugen i.c.m. zram gebruiken?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Gebruiker verwijderen"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Partitie (de)markeren voor versleuteling"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Wilt u wisselgeheugen i.c.m. zram gebruiken?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
#~ msgid "Add :"
#~ msgstr "Toevoegen:"
diff --git a/archinstall/locales/pl/LC_MESSAGES/base.mo b/archinstall/locales/pl/LC_MESSAGES/base.mo
index 7ca4f9b0..6044a910 100644
--- a/archinstall/locales/pl/LC_MESSAGES/base.mo
+++ b/archinstall/locales/pl/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po
index ec4fcbd4..30a5427a 100644
--- a/archinstall/locales/pl/LC_MESSAGES/base.po
+++ b/archinstall/locales/pl/LC_MESSAGES/base.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
-"Last-Translator: MedzikUser <medzik@duck.com>\n"
+"Last-Translator: acuteenvy\n"
"Language-Team: \n"
"Language: pl\n"
"MIME-Version: 1.0\n"
@@ -18,7 +18,7 @@ msgid " Please submit this issue (and file) to https://github.com/archlinux/a
msgstr " Proszę zgłosić ten błąd (i dołączyć plik) pod adresem https://github.com/archlinux/archinstall/issues"
msgid "Do you really want to abort?"
-msgstr "Czy napewno chcesz przerwać proces?"
+msgstr "Czy na pewno chcesz przerwać?"
msgid "And one more time for verification: "
msgstr "I jeszcze raz w celu weryfikacji: "
@@ -27,7 +27,7 @@ msgid "Would you like to use swap on zram?"
msgstr "Czy chcesz używać swap w zramie?"
msgid "Desired hostname for the installation: "
-msgstr "Nazwa hosta użyta do instalacji: "
+msgstr "Nazwa hosta dla tej instalacji: "
msgid "Username for required superuser with sudo privileges: "
msgstr "Nazwa użytkownika dla wymaganego superusera z uprawnieniami sudo: "
@@ -36,7 +36,7 @@ msgid "Any additional users to install (leave blank for no users): "
msgstr "Ewentualni użytkownicy do instalacji (pozostaw puste jeśli nie chcesz tworzyć użytkowników): "
msgid "Should this user be a superuser (sudoer)?"
-msgstr "Czy użytkownik powinien być superuserem (sudo)?"
+msgstr "Czy użytkownik powinien być superuserem (mieć uprawnienia sudo)?"
msgid "Select a timezone"
msgstr "Wybierz strefÄ™ czasowÄ…"
@@ -54,31 +54,31 @@ msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr
msgstr "Instalowane sÄ… tylko pakiety takie jak base, base-devel, linux, linux-firmware, efibootmgr i opcjonalne pakiety profili."
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "Jeśli wymagana jest przeglądarka internetowa, taka jak firefox lub chromium, można ją określić w następującym oknie dialogowym."
+msgstr "Jeśli potrzebujesz przeglądarki internetowej, takiej jak firefox lub chromium, możesz ją określić w następującym oknie dialogowym."
msgid "Write additional packages to install (space separated, leave blank to skip): "
msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw puste aby pominąć): "
msgid "Copy ISO network configuration to installation"
-msgstr "Skopiuj ustawienia sieciowe ISO do instalacji"
+msgstr "Skopiuj ustawienia sieciowe z ISO do instalacji"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)"
msgid "Select one network interface to configure"
msgstr "Wybierz jeden interfejs sieciowy do skonfigurowania"
msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
-msgstr "Wybierz tryb, który ma być skonfigurowany dla \"{}\" lub pomiń, aby użyć trybu domyślnego \"{}\""
+msgstr "Wybierz który tryb ma być skonfigurowany dla \"{}\" lub pomiń, aby użyć trybu domyślnego \"{}\""
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
-msgstr "Wprowadź adres IP i podsieć dla {}. (przykład: 192.168.0.5/24): "
+msgstr "Wprowadź adres IP i podsieć dla {} (przykład: 192.168.0.5/24): "
msgid "Enter your gateway (router) IP address or leave blank for none: "
-msgstr "Wprowadź adres IP bramy sieciowej (routera) (pozostaw puste pole w przypadku braku adresu): "
+msgstr "Wprowadź adres IP bramy sieciowej (routera) lub pozostaw puste: "
msgid "Enter your DNS servers (space separated, blank for none): "
-msgstr "Wpisz swoje serwery DNS (oddzielone spacjami, pozostaw puste w przypadku braku): "
+msgstr "Wpisz swoje serwery DNS (oddzielone spacjami, lub pozostaw puste): "
msgid "Select which filesystem your main partition should use"
msgstr "Wybierz, który system plików ma być używany na partycji głównej"
@@ -90,20 +90,20 @@ msgid ""
"Select what to do with\n"
"{}"
msgstr ""
-"Wybierz, co ma być zrobione z\n"
+"Wybierz, co zrobić z\n"
"{}"
msgid "Enter a desired filesystem type for the partition"
-msgstr "Wprowadź typ systemu plików dla partycji"
+msgstr "Wprowadź żądany typ systemu plików dla partycji"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Wybierz lokację startową (w jednostkach parted: s, GB, %, itd. ; domyślna: {})"
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Wybierz lokację końcową (w jednostkach parted: s, GB, %, itd. ; przykład: {})"
msgid "{} contains queued partitions, this will remove those, are you sure?"
-msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?"
+msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie. Czy na pewno chcesz to zrobić?"
msgid ""
"{}\n"
@@ -112,7 +112,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Wybierz według indeksu, które partycje mają zostać usunięte"
+"Wybierz według indeksu, które partycje usunąć"
msgid ""
"{}\n"
@@ -121,7 +121,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Wybierz według indeksu, które partycje mają zostać zamontowane"
+"Wybierz według indeksu, które partycje zamontować i gdzie"
msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
msgstr " * Punkty montowania partycji są względne w stosunku do wnętrza instalacji, np. partycja startowa to /boot."
@@ -169,7 +169,7 @@ msgid "Enter a desired filesystem type for the partition: "
msgstr "Wprowadź typ systemu plików dla partycji: "
msgid "Archinstall language"
-msgstr "Język Archinstall"
+msgstr "Język archinstall-a"
msgid "Wipe all selected drives and use a best-effort default partition layout"
msgstr "Wymaż wszystkie wybrane dyski i użyj najlepszego domyślnego układu partycji"
@@ -193,13 +193,13 @@ msgid "Select one or more hard drives to use and configure"
msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je"
msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem AMD, warto skorzystać z opcji całkowicie otawrto źródłowe (open-source) lub AMD / ATI."
+msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem AMD, warto skorzystać z opcji całkowicie open-source lub 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 "Aby uzyskać najlepszą kompatybilność ze sprzętem Intel, warto skorzystać z opcji całkowicie otawrto źródłowe (open-source) lub Intel.\n"
+msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem Intel, warto skorzystać z opcji całkowicie open-source lub Intel.\n"
msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
-msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem firmy Nvidia, warto skorzystać z firmowego sterownika firmy Nvidia.\n"
+msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem firmy Nvidia, warto skorzystać z zastrzeżonego sterownika firmy Nvidia.\n"
msgid ""
"\n"
@@ -208,22 +208,22 @@ msgid ""
msgstr ""
"\n"
"\n"
-"Wybierz sterownik graficzny lub pozostaw puste pole, aby zainstalować wszystkie sterowniki typu open source"
+"Wybierz sterownik graficzny lub pozostaw puste pole, aby zainstalować wszystkie sterowniki open-source"
msgid "All open-source (default)"
-msgstr "Wszystkie otwarto źródłowe (domyślnie)"
+msgstr "Wszystkie open-source (domyślnie)"
msgid "Choose which kernels to use or leave blank for default \"{}\""
-msgstr "Wybierz które jądra mają być używane, lub pozostaw puste dla ustawień domyślnych \"{}\""
+msgstr "Wybierz które jądra mają być używane, lub pozostaw puste, aby użyć ustawień domyślnych \"{}\""
msgid "Choose which locale language to use"
-msgstr "Wybierz które locale języka mają zostać użyte"
+msgstr "Wybierz języki, których chcesz używać"
msgid "Choose which locale encoding to use"
-msgstr "Wybierz które locale kodowania mają zostać użyte"
+msgstr "Wybierz kodowania, których chcesz używać"
msgid "Select one of the values shown below: "
-msgstr "Wybierz jedną z wartości przedstawionych poniżej: "
+msgstr "Wybierz jedną z poniższych wartości: "
msgid "Select one or more of the options below: "
msgstr "Wybierz jedną lub więcej z poniższych opcji: "
@@ -232,25 +232,25 @@ msgid "Adding partition...."
msgstr "Dodawanie partycji..."
msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
-msgstr "Aby kontynuować, musisz podać poprawny typ fs. Zobacz `man parted`, aby poznać prawidłowe typy fs."
+msgstr "Aby kontynuować, musisz podać poprawny fs-type. Zobacz `man parted`, aby poznać prawidłowe opcje."
msgid "Error: Listing profiles on URL \"{}\" resulted in:"
-msgstr "BÅ‚Ä…d: Lista profili z URL \"{}\":"
+msgstr "Błąd: Wyśwetlanie profili z URL \"{}\" spowodowało:"
msgid "Error: Could not decode \"{}\" result as JSON:"
-msgstr "Błąd: Nie można dekodować \"{}\" jako JSON:"
+msgstr "Błąd: Nie można zdekodować \"{}\" jako JSON:"
msgid "Keyboard layout"
msgstr "Układ klawiatury"
msgid "Mirror region"
-msgstr "Region lustra"
+msgstr "Region serwerów lustrzanych"
msgid "Locale language"
-msgstr "Locale języka"
+msgstr "Język"
msgid "Locale encoding"
-msgstr "Locale kodowania"
+msgstr "Kodowanie"
msgid "Drive(s)"
msgstr "Dyski twarde"
@@ -268,7 +268,7 @@ msgid "Bootloader"
msgstr "Program rozruchowy"
msgid "Root password"
-msgstr "Hasło użytkownika root"
+msgstr "Hasło roota"
msgid "Superuser account"
msgstr "Konto superusera"
@@ -292,7 +292,7 @@ msgid "Network configuration"
msgstr "Konfiguracja sieci"
msgid "Automatic time sync (NTP)"
-msgstr "Automatycznej synchronizacji czasu (NTP)"
+msgstr "Automatyczna synchronizacja czasu (NTP)"
msgid "Install ({} config(s) missing)"
msgstr "Zainstaluj ({} brakujÄ…cych konfiguracji)"
@@ -303,8 +303,8 @@ msgid ""
"WARNING: Archinstall won't check the suitability of this setup\n"
"Do you wish to continue?"
msgstr ""
-"Zdecydowałeś się pominąć wybór dysku twardego\n"
-"i użyć konfiguracji dysku zamontowanego w {} (eksperymentalne)\n"
+"Zdecydowano się na pominięcie wyboru dysku twardego\n"
+"i użycie konfiguracji dysku zamontowanego w {} (eksperymentalne)\n"
"OSTRZEŻENIE: Archinstall nie sprawdzi poprawności tej konfiguracji\n"
"Czy chcesz kontynuować?"
@@ -321,16 +321,16 @@ msgid "Clear/Delete all partitions"
msgstr "Wyczyść/Usuń wszystkie partycje"
msgid "Assign mount-point for a partition"
-msgstr "Przydzielanie punktu montowania dla partycji"
+msgstr "Przydziel punkt montowania dla partycji"
msgid "Mark/Unmark a partition to be formatted (wipes data)"
-msgstr "Zaznacz/odznacz partycję, która ma zostać sformatowana (wymazuje dane)"
+msgstr "Oznacz/odznacz partycję, która ma zostać sformatowana (wymazuje dane)"
msgid "Mark/Unmark a partition as encrypted"
-msgstr "Oznaczanie/odznaczanie partycji jako zaszyfrowanej"
+msgstr "Oznacz/odznacz partycjÄ™ jako zaszyfrowanÄ…"
msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
-msgstr "Oznaczanie/odznaczanie partycji jako startowe (rozruchowe/bootowalne) (automatyczne dla /boot)"
+msgstr "Oznacz/odznacz partycjÄ™ jako startowÄ… (rozruchowÄ…/bootowalnÄ…) (automatyczne dla /boot)"
msgid "Set desired filesystem for a partition"
msgstr "Ustaw system plików dla partycji"
@@ -348,7 +348,7 @@ msgid "Timezone"
msgstr "Strefa czasowa"
msgid "Set/Modify the below options"
-msgstr "Ustaw/zmodyfikuj poniższe opcje"
+msgstr "Ustaw/modyfikuj poniższe opcje"
msgid "Install"
msgstr "Zainstaluj"
@@ -361,7 +361,7 @@ msgstr ""
"\n"
msgid "Suggest partition layout"
-msgstr "Sugerowany układ partycji"
+msgstr "Zasugeruj układ partycji"
msgid "Enter a password: "
msgstr "Wprowadź hasło: "
@@ -382,7 +382,7 @@ msgid "Password for user \"{}\": "
msgstr "Hasło użytkownika \"{}\": "
msgid "Verifying that additional packages exist (this might take a few seconds)"
-msgstr "Sprawdzenie, czy istnieją dodatkowe pakiety (może to potrwać kilka sekund)"
+msgstr "Sprawdzanie, czy istnieją dodatkowe pakiety (może to potrwać kilka sekund)"
msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
msgstr "Czy chcesz korzystać z automatycznej synchronizacji czasu (NTP) z domyślnymi serwerami czasu?\n"
@@ -405,7 +405,7 @@ msgid ""
" Choose an object from the list, and select one of the available actions for it to execute"
msgstr ""
"\n"
-" Wybierz obiekt z listy, a następnie wybierz jedną z dostępnych akcji do wykonania"
+" Wybierz obiekt z listy, a następnie wybierz jedno z dostępnych działań do wykonania"
msgid "Cancel"
msgstr "Anuluj"
@@ -426,13 +426,13 @@ msgid "Delete"
msgstr "Usuń"
msgid "Select an action for '{}'"
-msgstr "Wybierz akcjÄ™ dla '{}'"
+msgstr "Wybierz działanie dla '{}'"
msgid "Copy to new key:"
msgstr "Skopiuj do nowego klucza:"
msgid "Unknown nic type: {}. Possible values are {}"
-msgstr "Nieznany typ niszy: {}. Możliwe wartości to {}"
+msgstr "Nieznany typ nic: {}. Możliwe wartości to {}"
msgid ""
"\n"
@@ -442,13 +442,13 @@ msgstr ""
"Wybrana konfiguracja:"
msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
-msgstr "Pacman jest już uruchomiony i czeka maksymalnie 10 minut na zakończenie pracy."
+msgstr "Pacman jest już uruchomiony, czekam maksymalnie 10 minut na zakończenie pracy."
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "Istniejąca wcześniej blokada programu pacman nie została zakończona. Proszę wyczyścić wszystkie istniejące sesje pacmana przed użyciem archinstall."
+msgstr "Istniejąca wcześniej blokada pacmana nie została zakończona. Proszę wyczyścić wszystkie istniejące sesje pacmana przed użyciem archinstall-a."
msgid "Choose which optional additional repositories to enable"
-msgstr "Wybierz, które z opcjonalnych dodatkowych repozytoriów mają być włączone"
+msgstr "Wybierz, które z opcjonalnych repozytoriów chcesz włączyć"
msgid "Add a user"
msgstr "Dodaj użytkownika"
@@ -457,7 +457,7 @@ msgid "Change password"
msgstr "Zmień hasło"
msgid "Promote/Demote user"
-msgstr "Promuj/usuń użytkownika"
+msgstr "Promuj/degraduj użytkownika"
msgid "Delete User"
msgstr "Usuń użytkownika"
@@ -473,10 +473,10 @@ msgid "User Name : "
msgstr "Nazwa użytkownika : "
msgid "Should {} be a superuser (sudoer)?"
-msgstr "Czy {} powinien być superuserem (sudoer)?"
+msgstr "Czy użytkownik {} powinien być superuserem (mieć uprawnienia sudo)?"
msgid "Define users with sudo privilege: "
-msgstr "Zdefiniuj użytkowników z przywilejem sudo: "
+msgstr "Zdefiniuj użytkowników z uprawnieniami sudo: "
msgid "No network configuration"
msgstr "Brak konfiguracji sieciowej"
@@ -497,7 +497,7 @@ msgid "No configuration"
msgstr "Brak konfiguracji"
msgid "Save user configuration"
-msgstr "Zapisz konfiguracje użytkownika"
+msgstr "Zapisz konfigurację użytkownika"
msgid "Save user credentials"
msgstr "Zapisz dane uwierzytelniające użytkownika"
@@ -515,7 +515,7 @@ msgid "Enter a directory for the configuration(s) to be saved: "
msgstr "Wprowadź katalog, w którym ma zostać zapisana konfiguracja: "
msgid "Not a valid directory: {}"
-msgstr "Nie jest to prawidłowy katalog: {}"
+msgstr "Nieprawidłowy katalog: {}"
msgid "The password you are using seems to be weak,"
msgstr "Używane przez Ciebie hasło wydaje się być słabe,"
@@ -524,19 +524,19 @@ msgid "are you sure you want to use it?"
msgstr "czy na pewno chcesz go używać?"
msgid "Optional repositories"
-msgstr "Dodatkowe repozytoria"
+msgstr "Opcjonalne repozytoria"
msgid "Save configuration"
-msgstr "Zapisz konfiguracje"
+msgstr "Zapisz konfiguracjÄ™"
msgid "Missing configurations:\n"
-msgstr "Brak konfiguracji:\n"
+msgstr "BrakujÄ…ce konfiguracje:\n"
msgid "Either root-password or at least 1 superuser must be specified"
-msgstr "Musi być podane albo hasło root, albo co najmniej jednego superusera"
+msgstr "Należy podać hasło roota lub stworzyć co najmniej jednego superusera"
msgid "Manage superuser accounts: "
-msgstr "ZarzÄ…dzaj kontami superusera: "
+msgstr "Zarządzaj kontami superuserów: "
msgid "Manage ordinary user accounts: "
msgstr "Zarządzaj kontami zwykłych użytkowników: "
@@ -555,28 +555,28 @@ msgid ""
" Fill the desired values for a new subvolume \n"
msgstr ""
"\n"
-" Wypełnij żądane wartości dla nowego subwolumenu \n"
+" Wypełnij żądane wartości dla nowego subwoluminu \n"
msgid "Subvolume name "
-msgstr "Nazwa subwolumenu "
+msgstr "Nazwa subwoluminu "
msgid "Subvolume mountpoint"
-msgstr "Punkt montowania subwolumenu"
+msgstr "Punkt montowania subwoluminu"
msgid "Subvolume options"
-msgstr "Opcje subwolumenu"
+msgstr "Opcje subwoluminu"
msgid "Save"
msgstr "Zapisz"
msgid "Subvolume name :"
-msgstr "Nazwa subwolumenu :"
+msgstr "Nazwa subwoluminu :"
msgid "Select a mount point :"
msgstr "Wybierz punkt montowania :"
msgid "Select the desired subvolume options "
-msgstr "Wybierz opcje subwolumenu "
+msgstr "Wybierz opcje subwoluminu "
msgid "Define users with sudo privilege, by username: "
msgstr "Określanie użytkowników z uprawnieniami sudo według nazwy użytkownika: "
@@ -585,7 +585,7 @@ msgid "[!] A log file has been created here: {}"
msgstr "[!] Plik dziennika został zapisany tutaj: {}"
msgid "Would you like to use BTRFS subvolumes with a default structure?"
-msgstr "Czy chcesz użyć subwolumenów BTRFS z domyślną strukturą?"
+msgstr "Czy chcesz użyć subwoluminów BTRFS z domyślną strukturą?"
msgid "Would you like to use BTRFS compression?"
msgstr "Czy chcesz użyć kompresji BTRFS?"
@@ -594,7 +594,7 @@ msgid "Would you like to create a separate partition for /home?"
msgstr "Czy chcesz stworzyć oddzielną partycje dla /home?"
msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
-msgstr "Wybrane dyski nie mają wymaganej pojemności dla automatycznej sugestii\n"
+msgstr "Wybrane dyski nie mają minimalnej wymaganej pojemności dla automatycznej sugestii\n"
msgid "Minimum capacity for /home partition: {}GB\n"
msgstr "Maksymalna pojemność dla partycji /home: {}GB\n"
@@ -612,13 +612,13 @@ msgid "no"
msgstr "nie"
msgid "set: {}"
-msgstr "ustawienie: {}"
+msgstr "ustawiono na: {}"
msgid "Manual configuration setting must be a list"
msgstr "Konfiguracja ustawiona manualnie musi być listą"
msgid "No iface specified for manual configuration"
-msgstr "Iface nie zostało ustawione w ręcznej konfiguracji"
+msgstr "Nie określono interfejsu do ręcznej konfiguracji"
msgid "Manual nic configuration with no auto DHCP requires an IP address"
msgstr "Ręczna konfiguracja nic bez automatycznego DHCP wymaga podania adresu IP"
@@ -633,7 +633,7 @@ msgid "Delete interface"
msgstr "Usuń interfejs"
msgid "Select interface to add"
-msgstr "Wybierz interfejs sieciowy do skonfigurowania"
+msgstr "Wybierz interfejs sieciowy do dodania"
msgid "Manual configuration"
msgstr "Ręczna konfiguracja"
@@ -645,7 +645,7 @@ msgid "The password you are using seems to be weak, are you sure you want to use
msgstr "Używane przez Ciebie hasło wydaje się być słabe, czy na pewno chcesz go użyć?"
msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
-msgstr "Dostarcza wybór środowisk graficznych oraz menedżerów okien, np. gnome, kde, sway"
+msgstr "Dostarcza wybór środowisk graficznych oraz kafelkowych menedżerów okien, np. gnome, kde, sway"
msgid "Select your desired desktop environment"
msgstr "Wybierz środowisko graficzne"
@@ -657,16 +657,16 @@ msgid "Provides a selection of various server packages to install and enable, e.
msgstr "Dostarcza wybór różnych pakietów serwerowych do zainstalowania i uruchomienia, np. httpd, nginx, mariadb"
msgid "Choose which servers to install, if none then a minimal installation will be done"
-msgstr "Wybierz jakie serwery zainstalować. Jeżeli żadne, wtedy będzie użyta minimalna instalacja"
+msgstr "Wybierz jakie serwery zainstalować. Jeżeli żadne, wykonana będzie minimalna instalacja"
msgid "Installs a minimal system as well as xorg and graphics drivers."
-msgstr "Instaluje system podstawowy z xorg i sterownikami graficznymi."
+msgstr "Instaluje system podstawowy, a także xorg-a i sterowniki graficzne."
msgid "Press Enter to continue."
msgstr "Naciśnij Enter, aby kontynuować."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "Czy chciałbyś zchrootować do nowej instalacji i przeprowadzić wstępną konfigurację?"
+msgstr "Czy chcesz zchrootować do nowej instalacji i przeprowadzić dodatkową konfigurację?"
msgid "Are you sure you want to reset this setting?"
msgstr "Czy na pewno chcesz zresetować to ustawienie?"
@@ -678,18 +678,17 @@ msgid "Any modifications to the existing setting will reset the disk layout!"
msgstr "Każda zmiana istniejących ustawień zresetuje układ dysków!"
msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
-msgstr "Jeżeli zresetujesz wybór dysków, zresetujesz także obecny układ dysków. Jesteś pewny, że chcesz to zrobić?"
+msgstr "Jeżeli zresetujesz wybór dysków, zresetujesz także obecny układ dysków. Czy na pewno chcesz to zrobić?"
msgid "Save and exit"
msgstr "Zapisz i wyjdź"
-#, fuzzy
msgid ""
"{}\n"
"contains queued partitions, this will remove those, are you sure?"
msgstr ""
"{}\n"
-"zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?"
+"zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie. Czy na pewno chcesz to zrobić?"
msgid "No audio server"
msgstr "Brak serwera dźwięku"
@@ -710,7 +709,6 @@ msgstr ""
msgid "Copy to: "
msgstr "Kopiuj do: "
-#, fuzzy
msgid "Edit: "
msgstr "Edytuj: "
@@ -726,9 +724,8 @@ msgstr "Dodaj: "
msgid "Value: "
msgstr "Wartość: "
-#, fuzzy
msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
-msgstr "Możesz pominąć wybór dysków i partycji i użyć dowolnego zestawu dysku zamontowanego w /mnt (eksperymentalne)"
+msgstr "Możesz pominąć wybór dysku i partycjonowanie i użyć konfiguracji dysku zamontowanego w /mnt (eksperymentalne)"
msgid "Select one of the disks or skip and use /mnt as default"
msgstr "Wybierz jeden z dysków lub pomiń i użyj /mnt jako domyślnego"
@@ -752,7 +749,7 @@ msgid "Bus-type"
msgstr "Typ magistrali"
msgid "Either root-password or at least 1 user with sudo privileges must be specified"
-msgstr "Musisz podać hasło root lub utworzyć co najmniej jednego superusera"
+msgstr "Musisz podać hasło roota lub utworzyć co najmniej jednego superusera"
msgid "Enter username (leave blank to skip): "
msgstr "Wprowadź nazwę użytkownika (pozostaw puste, aby pominąć): "
@@ -761,11 +758,10 @@ msgid "The username you entered is invalid. Try again"
msgstr "Wprowadzona nazwa użytkownika jest nieprawidłowa. Spróbuj ponownie"
msgid "Should \"{}\" be a superuser (sudo)?"
-msgstr "Czy \"{}\" powinien być superuserem (sudo)?"
+msgstr "Czy użytkownik \"{}\" powinien być superuserem (mieć uprawnienia sudo)?"
-#, fuzzy
msgid "Select which partitions to encrypt"
-msgstr "Wybierz partycja która ma zostać zaszyfrowana"
+msgstr "Wybierz, które partycje mają zostać zaszyfrowane"
msgid "very weak"
msgstr "bardzo słabe"
@@ -777,48 +773,49 @@ msgid "moderate"
msgstr "umiarkowane"
msgid "strong"
-msgstr "mocne"
+msgstr "silne"
msgid "Add subvolume"
-msgstr "Dodaj podwolumen"
+msgstr "Dodaj subwolumin"
msgid "Edit subvolume"
-msgstr "Edytuj podwolumen"
+msgstr "Edytuj subwolumin"
msgid "Delete subvolume"
-msgstr "Usuń podwolumen"
+msgstr "Usuń subwolumin"
msgid "Configured {} interfaces"
msgstr "Skonfigurowano {} interfejsów"
msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji"
+msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji"
-#, fuzzy, python-brace-format
+#, fuzzy
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Wprowadź maksymalną liczbę plików pobieranych jednocześnie.\n"
-" (Liczba z zakresu od 1 do {max_downloads})\n"
+" (Liczba z zakresu od 1 do {})\n"
"Zauważ:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Maksymalna wartość : {max_downloads} ( Zwiększa liczbę zadań o {max_downloads}, co pozwala na pobieranie {max_downloads+1} plików jednocześnie )"
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwala na pobieranie {} plików jednocześnie )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Minimalna wartość : 1 ( Zwiększa liczbę zadań o 1, co pozwala na pobieranie 2 plików jednocześnie )"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr " - Wyłącz/domyślnie : 0 ( Wyłącza pobieranie równoległe, więc tylko 1 plik może być pobierany w tym czasie )"
+msgstr " - Wyłącz/Domyślne : 0 ( Wyłącza pobieranie wielu plików jednocześnie, więc tylko 1 plik może być pobierany w tym samym czasie )"
-#, fuzzy, python-brace-format
+#, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {max_downloads}, lub 0 aby wyłączyć."
+msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć."
msgid "Parallel Downloads"
-msgstr "Pobieranie równoległe"
+msgstr "Pobieranie kilku plików jednocześnie"
msgid "ESC to skip"
msgstr "Naciśnij ESC, aby pominąć"
@@ -826,62 +823,498 @@ msgstr "Naciśnij ESC, aby pominąć"
msgid "CTRL+C to reset"
msgstr "Naciśnij Ctrl+C, aby zresetować"
-#, fuzzy
msgid "TAB to select"
msgstr "Naciśnij Tab, aby wybrać"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[Domyślna wartość: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Aby móc skorzystać z tego tłumaczenia, proszę ręcznie zainstalować czcionkę, która obsługuje ten język."
msgid "The font should be stored as {}"
-msgstr ""
+msgstr "Czcionka powinna być przechowana jako {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall wymaga uprawnień administratora do uruchomienia. Wpisz --help, aby uzyskać więcej informacji."
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "Wybierz akcjÄ™ dla '{}'"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Nie można pobrać profilu z podanego url: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Profile muszą mieć unikalne nazwy, znaleziono profil o tej nazwie: {}"
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Jeżeli zresetujesz wybór dysków, zresetujesz także obecny układ dysków. Jesteś pewny, że chcesz to zrobić?"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "Dodawanie partycji..."
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "Usuń partycję"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Wprowadź katalog, w którym ma zostać zapisana konfiguracja: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Maksymalna pojemność dla partycji /home: {}GB\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Minimalna pojemność dla partycji Arch Linux: {}GB"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "To jest lista wstępnie zaprogramowanych profili, które mogą ułatwić instalację takich rzeczy jak środowiska graficzne"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "Aktualny układ partycji"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "Utwórz nową partycję"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "Przydzielanie punktu montowania dla partycji"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Zaznacz/odznacz partycję, która ma zostać sformatowana (wymazuje dane)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Zaznacz/Odznacz jako bootowalne"
+
+msgid "Change filesystem"
+msgstr "Zmień system plików"
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "Oznacz/odznacz partycje jako skompresowanÄ… (tylko w btrfs)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "Usuń podwolumen"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "Usuń partycję"
+
+msgid "Partition"
+msgstr "Partycja"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Wybrana partycja jest zaszyfrowana. Żeby ją sformatować, wybierz system plików."
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Punkty montowania partycji są względne w stosunku do wnętrza instalacji, np. partycja startowa to /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Jeżeli punkt montowania /boot jest wybrany, ta partycja będzie także zaznaczona jako bootowalna"
+
+msgid "Mountpoint: "
+msgstr "Punkt montowania: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Aktualnie wolne sektory urzÄ…dzenia {}:"
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "Nie jest to prawidłowy katalog: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Wprowadź sektor końcowy (procent lub numer bloku, domyślnie: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "To usunie wszystkie nowo dodane partycje, kontynuować?"
+
+msgid "Partition management: {}"
+msgstr "ZarzÄ…dzanie partycjÄ…: {}"
+
+msgid "Total length: {}"
+msgstr "Całkowita długość: {}"
#, fuzzy
msgid "Encryption type"
-msgstr "Hasło szyfrujące"
+msgstr "Typ szyfrowania"
msgid "Partitions"
-msgstr ""
+msgstr "Partycje"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Brak dostępnych urządzeń HSM"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Wybierz partycja która ma zostać zaszyfrowana"
+msgstr "Partycje do zaszyfrowania"
-#, fuzzy
msgid "Select disk encryption option"
-msgstr "Wybierz układ dysku"
+msgstr "Wybierz opcjÄ™ szyfrowania dysku"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Wybierz urządzenie FIDO2 do użycia z HSM"
+
+#, fuzzy
+msgid "Use a best-effort default partition layout"
+msgstr "Wymaż wszystkie wybrane dyski i użyj najlepszego domyślnego układu partycji"
+
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "Ręczna konfiguracja"
#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "Brak konfiguracji"
+
+msgid "Unknown"
+msgstr "Nieznane"
+
+msgid "Partition encryption"
+msgstr "Szyfrowanie partycji"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formatowanie {} za "
+
+msgid "↠Back"
+msgstr "↠Wstecz"
+
+msgid "Disk encryption"
+msgstr "Szyfrowanie dysku"
+
+#, fuzzy
+msgid "Configuration"
+msgstr "Brak konfiguracji"
+
+#, fuzzy
+msgid "Password"
+msgstr "Hasło"
+
msgid "All settings will be reset, are you sure?"
-msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?"
+msgstr "Wszystkie ustawienia zostaną zresetowane. Czy na pewno chcesz to zrobić?"
+#, fuzzy
msgid "Back"
+msgstr "↠Wstecz"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Wybierz który greeter zainstalować dla wybranych profili: {}"
+
+msgid "Environment type: {}"
+msgstr "Rodzaj środowiska: {}"
+
+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 "Zastrzeżony sterownik Nvidia nie jest wspierany przez Sway. Mogą wystąpić problemy, czy chcesz kontynuować?"
+
+#, fuzzy
+msgid "Installed packages"
+msgstr "Dodatkowe pakiety"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "Profil"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "Profil"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "Usuń interfejs"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "Profil"
+
+#, fuzzy
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Wprowadzona nazwa użytkownika jest nieprawidłowa. Spróbuj ponownie"
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw puste aby pominąć): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw puste aby pominąć): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Czy ten profil ma być włączony podczas instalacji?"
+
+msgid "Create your own"
+msgstr "Utwórz własny"
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"\n"
+"Wybierz sterownik graficzny lub pozostaw puste pole, aby zainstalować wszystkie sterowniki typu open source"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway potrzebuje dostępu do twojego stanowia (sprzętu, np. klawiatury, myszki)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Wybierz by nadać Sway dostęp do twojego sprzętu"
+
+msgid "Graphics driver"
+msgstr "Sterownik graficzny"
+
+msgid "Greeter"
+msgstr "Greeter"
+
+msgid "Please chose which greeter to install"
+msgstr "Wybierz który greeter zainstalować"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "To jest lista przygotowanych podstawowych profili (default_profiles)"
#, fuzzy
-msgid "Password"
-msgstr "Hasło użytkownika root"
+msgid "Disk configuration"
+msgstr "Brak konfiguracji"
-msgid "Partition encryption"
+#, fuzzy
+msgid "Profiles"
+msgstr "Profil"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Znajdywanie możliwych katalogów do zapisywania plików konfiguracyjnych ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Wybierz jeden lub więcej katalogów do zapisywania plików konfiguracyjnych"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "Dodaj użytkownika"
+
+msgid "Change custom mirror"
+msgstr "Ustaw własny mirror"
+
+msgid "Delete custom mirror"
+msgstr "Usuń własny mirror"
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "Wprowadź nazwę użytkownika (pozostaw puste, aby pominąć): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "Wprowadź nazwę użytkownika (pozostaw puste, aby pominąć): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "Wybierz układ dysku"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "Wybierz układ dysku"
+
+msgid "Custom mirrors"
+msgstr "WÅ‚asny mirror"
+
+msgid "Defined"
+msgstr "Wybrane"
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "Zapisz konfiguracje użytkownika"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "Wprowadź katalog, w którym ma zostać zapisana konfiguracja: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
msgstr ""
+"Czy chcesz zapisać plik(i) konfiguracji {} w podanej lokalizacji?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Zapisywanie {} plików konfiguracyjnych do {}"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "Region lustra"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "Region serwerów lustrzanych"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): "
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwala na pobieranie {} plików jednocześnie )"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć."
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)"
+
+msgid "Total: {} / {}"
+msgstr "ÅÄ…cznie: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Do wszystkich wybranych wartości musi być podana jednostka: B, KB, KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Jeżeli jednostka nie zostanie poddana, wartość zostanie zinterpretowana jako sektor"
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Nie można określić urządzeń fido2. Czy zainstalowano libfido2?"
+
+msgid "Path"
+msgstr "Ścieżka"
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Ręczna konfiguracja"
+
+msgid "Type"
+msgstr "Rodzaj"
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Wprowadź maksymalną liczbę plików pobieranych jednocześnie.\n"
+" (Liczba z zakresu od 1 do {})\n"
+"Zauważ:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwala na pobieranie {} plików jednocześnie )"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Wyłącz/Domyślne : 0 ( Wyłącza pobieranie wielu plików jednocześnie, więc tylko 1 plik może być pobierany w tym samym czasie )"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć."
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland potrzebuje dostępu do twojego stanowia (sprzętu, np. klawiatury, myszki)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Wybierz opcje, żeby dać Hyprland dostęp do twojego urządzenia"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Do wszystkich wybranych wartości może być podana jednostka: % B, KB, KiB, MB, MiB..."
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Czy chcesz użyć zjednoczonych obrazów jądra? (unified kernel images)"
+
+msgid "Unified kernel images"
+msgstr "zjednoczone obrazy jÄ…dra (unified kernel images)"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Oczekiwanie na synchronizacjÄ™ czasu (timedatectl show)."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Synchronizacja czasu w toku. Oczekując - sprawdź dokumentacje: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Pomiń Oczekiwanie na automatyczną synchronizację czasu (może spowodować problemy podczas instalacji)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Oczekiwanie na synchronizacjÄ™ Arch Linux keyring (archlinux-keyring-wkd-sync)."
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Usuń interfejs"
+
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Synchronizacja czasu w toku. Oczekując - sprawdź dokumentacje: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Zaznacz/Odznacz jako bootowalne"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Czy chcesz użyć kompresji BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Wprowadź sektor końcowy (procent lub numer bloku, domyślnie: {}): "
+#~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: "
+#~ msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: "
+
+#~ msgid ""
+#~ "Do you want to save {} configuration file(s) in the following locations?\n"
+#~ "\n"
+#~ "{}"
+#~ msgstr ""
+#~ "Czy chcesz zapisać {} plików konfiguracyjnych do następujących lokalizacji?\n"
+#~ "\n"
+#~ "{}"
#~ msgid "Add :"
#~ msgstr "Dodaj :"
diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo
index 99fd4b02..aea7fe45 100644
--- a/archinstall/locales/pt/LC_MESSAGES/base.mo
+++ b/archinstall/locales/pt/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po
index 4f772aae..0ab61c95 100644
--- a/archinstall/locales/pt/LC_MESSAGES/base.po
+++ b/archinstall/locales/pt/LC_MESSAGES/base.po
@@ -1,20 +1,25 @@
+# Translators:
+# Hugo Carvalho <hugokarvalho@hotmail.com>
+#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
-"Language-Team: \n"
-"Language: es\n"
+"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
+"Language-Team: Portuguese\n"
+"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.0.1\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.4.1\n"
msgid "[!] A log file has been created here: {} {}"
-msgstr "[!] Um arquivo de log foi criado aqui: {} {}"
+msgstr "[!] Foi criado um ficheiro de registo aqui: {} {}"
msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
-msgstr " Por favor envie este problema (e arquivo) para https://github.com/archlinux/archinstall/issues"
+msgstr " Submeta este problema (e ficheiro) para https://github.com/archlinux/archinstall/issues"
msgid "Do you really want to abort?"
msgstr "Queres mesmo abortar?"
@@ -23,64 +28,64 @@ msgid "And one more time for verification: "
msgstr "Uma última vez para verificação: "
msgid "Would you like to use swap on zram?"
-msgstr "Gostarias de usar swap em zram?"
+msgstr "Gostaria de usar a swap em zram?"
msgid "Desired hostname for the installation: "
-msgstr "Hostname desejado para a instalação: "
+msgstr "Nome do computador desejado para a instalação: "
msgid "Username for required superuser with sudo privileges: "
-msgstr "Nome de utilizador para o super-utilizador com privilégios sudo: "
+msgstr "Nome de utilizador para o superutilizador com privilégios sudo: "
msgid "Any additional users to install (leave blank for no users): "
msgstr "Algum utilizador adicional para instalar (deixa em branco para nenhum utilizador): "
msgid "Should this user be a superuser (sudoer)?"
-msgstr "Deve este utilizador ser um super-utilizador (sudoer)?"
+msgstr "Deve este utilizador ser um superutilizador (sudoer)?"
msgid "Select a timezone"
-msgstr "Seleciona um fuso horário"
+msgstr "Selecione um fuso horário"
msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
-msgstr "Preferias usar o GRUB como bootloader ao invés de systemd-boot?"
+msgstr "Gostaria de utilizar o GRUB como carregador de arranque em vez do systemd-boot?"
msgid "Choose a bootloader"
-msgstr "Escolhe um bootloader"
+msgstr "Escolha um carregador de arranque"
msgid "Choose an audio server"
-msgstr "Escolhe um servidor de áudio"
+msgstr "Escolha um servidor de áudio"
msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
msgstr "Apenas pacotes como base, base-devel, linux, linux-firmware, efibootmgr e pacotes opcionais de perfil são instalados."
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "Se queres um navegador web, como firefox ou chromium, deves especificá-lo na próxima pergunta."
+msgstr "Se quer um navegador web, como firefox ou chromium, deve especificá-lo na próxima pergunta."
msgid "Write additional packages to install (space separated, leave blank to skip): "
-msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa em branco para não instalar nada): "
+msgstr "Inserir pacotes adicionais a instalar (separados por espaço, deixar em branco para ignorar): "
msgid "Copy ISO network configuration to installation"
-msgstr "Copiar a configuração de rede do ISO para a instalação"
+msgstr "Copiar a configuração de rede da ISO para a instalação"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
-msgstr "Usar NetworkManager (necessário para configurar internet graficamente em GNOME e KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "Usar o Gestor de Redes \"NetworkManager\" (necessário para configurar Internet graficamente em GNOME e KDE)"
msgid "Select one network interface to configure"
msgstr "Selecione uma interface de rede para configurar"
msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
-msgstr "Selecciona qual modo configurar para \"{}\" ou pula para usar o modo padrão \"{}\""
+msgstr "Selecionar qual o modo a configurar para \"{}\" ou ignorar para usar o modo predefinido \"{}\""
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
-msgstr "Escreve a IP e subrede para {} (exemplo: 192.168.0.5/24): "
+msgstr "Inserir o IP e subrede para {} (exemplo: 192.168.0.5/24): "
msgid "Enter your gateway (router) IP address or leave blank for none: "
-msgstr "Escreve a tua IP de gateway (router) ou deixa em branco para nenhuma: "
+msgstr "Inserir o seu IP de gateway (router) ou deixe em branco para nenhum: "
msgid "Enter your DNS servers (space separated, blank for none): "
-msgstr "Escreve os servidores DNS (separados por espaço, deixa em branco para nenhum): "
+msgstr "Inserir os servidores DNS (separados por espaço, deixe em branco para nenhum): "
msgid "Select which filesystem your main partition should use"
-msgstr "Seleciona qual sistema de ficheiros a partição principal deverá usar"
+msgstr "Selecionar o sistema de ficheiros que a partição principal deve utilizar"
msgid "Current partition layout"
msgstr "Esquema actual da partições"
@@ -93,16 +98,16 @@ msgstr ""
"{}"
msgid "Enter a desired filesystem type for the partition"
-msgstr "Escreve o tipo de sistema de ficheiros desejado para a partição"
+msgstr "Inserir o tipo de sistema de ficheiros desejado para a partição"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Inserir o local inicial (em unidades do parted: s, GB, %, etc. ; predefinido: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Inserir o local final (em unidades do parted: s, GB, %, etc. ; predefinido: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
-msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?"
+msgstr "{} contem partições em fila, isto irá remover essas, tem a certeza?"
msgid ""
"{}\n"
@@ -171,7 +176,7 @@ msgid "Archinstall language"
msgstr "Idioma do Archinstall"
msgid "Wipe all selected drives and use a best-effort default partition layout"
-msgstr "Limpar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho"
+msgstr "Limpar todos os discos selecionados e usar um esquema de partições predefinido de melhor desempenho"
msgid "Select what to do with each individual drive (followed by partition usage)"
msgstr "Seleciona o que fazer com cada disco individual (seguido de uso de partição)"
@@ -186,19 +191,19 @@ msgid "Select keyboard layout"
msgstr "Seleciona o esquema de teclado"
msgid "Select one of the regions to download packages from"
-msgstr "Seleciona uma das regiões para descarregar pacotes"
+msgstr "Selecione uma das regiões a partir da qual pretende transferir pacotes"
msgid "Select one or more hard drives to use and configure"
msgstr "Seleciona um ou mais discos rígidos para usar e configurar"
msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "Para uma melhor compatibilidade com o teu hardware AMD, poderás querer usar a opção de todos os drivers open-source ou com proprietários da AMD / ATI."
+msgstr "Para uma melhor compatibilidade com o teu hardware AMD, poderás querer usar a opção de todos os controladores de código-aberto ou com proprietários da AMD / ATI."
msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n"
-msgstr "Para uma melhor compatibilidade com o teu hardware Intel, poderás querer usar a opção de todos os drivers open-source ou com proprietários da Intel.\n"
+msgstr "Para uma melhor compatibilidade com o teu hardware Intel, poderás querer usar a opção de todos os controladores de código-aberto ou com proprietários da Intel.\n"
msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
-msgstr "Para uma melhor compatibilidade com o teu hardware Nvidia, poderás querer usar o driver proprietário da Nvidia.\n"
+msgstr "Para uma melhor compatibilidade com o teu hardware Nvidia, poderás querer usar o controlador proprietário da Nvidia.\n"
msgid ""
"\n"
@@ -207,13 +212,13 @@ msgid ""
msgstr ""
"\n"
"\n"
-"Seleciona um driver de gráficos ou deixa em branco para instalar todos os drivers open-source"
+"Seleciona um controlador de gráficos ou deixa em branco para instalar todos os controladores de código-aberto"
msgid "All open-source (default)"
-msgstr "Todos os drivers open-source (padrão)"
+msgstr "Todos os controladores de código-aberto (predefinido)"
msgid "Choose which kernels to use or leave blank for default \"{}\""
-msgstr "Escolhe quais kernels usar ou deixa em branco para o padrão \"{}\""
+msgstr "Escolha os kernels a usar ou deixe em branco para a predefinição \"{}\""
msgid "Choose which locale language to use"
msgstr "Escolhe qual idioma de localização usar"
@@ -234,16 +239,16 @@ msgid "You need to enter a valid fs-type in order to continue. See `man parted`
msgstr "Precisas de colocar um tipo de sistema de ficheiros valido. Consulta o `man parted` para ver as opções validas."
msgid "Error: Listing profiles on URL \"{}\" resulted in:"
-msgstr "Erro: Listando os perfis em URL \"{}\" resulta em:"
+msgstr "Erro: a listar os perfis em URL \"{}\" resulta em:"
msgid "Error: Could not decode \"{}\" result as JSON:"
-msgstr "Erro: Não foi possível decodificar \"{}\" como JSON:"
+msgstr "Erro: não foi possível decodificar \"{}\" como JSON:"
msgid "Keyboard layout"
msgstr "Esquema do teclado"
msgid "Mirror region"
-msgstr "Região do mirror"
+msgstr "Região do espelho"
msgid "Locale language"
msgstr "Idioma de localização"
@@ -252,15 +257,13 @@ msgid "Locale encoding"
msgstr "Codificação de localização"
msgid "Drive(s)"
-msgstr "Discos rígidos"
+msgstr "Unidade(s)"
-#, fuzzy
msgid "Disk layout"
-msgstr "Selecionar esquema de disco"
+msgstr "Esquema do disco"
-#, fuzzy
msgid "Encryption password"
-msgstr "Define a palavra-passe de encriptação"
+msgstr "Palavra-passe de encriptação"
msgid "Swap"
msgstr "Swap"
@@ -268,15 +271,14 @@ msgstr "Swap"
msgid "Bootloader"
msgstr "Carregador de arranque"
-#, fuzzy
msgid "Root password"
msgstr "Palavra-passe de root"
msgid "Superuser account"
-msgstr "Conta de superusuário"
+msgstr "Conta de superutilizador"
msgid "User account"
-msgstr "Conta de usuário"
+msgstr "Conta de utilizador"
msgid "Profile"
msgstr "Perfil"
@@ -305,13 +307,13 @@ msgid ""
"WARNING: Archinstall won't check the suitability of this setup\n"
"Do you wish to continue?"
msgstr ""
-"Decidiste ignorar a seleção de disco rígido\n"
+"Decidiu ignorar a seleção de disco rígido\n"
"e usar qualquer configuração de disco rígido montada em {} (experimental)\n"
-"ATENÇÃO: Archinstall não verificar a viabilidade desta configuração\n"
-"Deseja continuar?"
+"ATENÇÃO: O archinstall não verifica a viabilidade desta configuração\n"
+"Quer continuar?"
msgid "Re-using partition instance: {}"
-msgstr "Reutilizando a instância da partição: {}"
+msgstr "A reutilizar a instância da partição: {}"
msgid "Create a new partition"
msgstr "Criar uma nova partição"
@@ -399,11 +401,8 @@ msgstr ""
msgid "Enter a username to create an additional user (leave blank to skip): "
msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): "
-#, fuzzy
msgid "Use ESC to skip\n"
-msgstr ""
-"Usa ESC para saltar\n"
-"\n"
+msgstr "Use ESC para ignorar\n"
msgid ""
"\n"
@@ -415,9 +414,8 @@ msgstr ""
msgid "Cancel"
msgstr "Cancelar"
-#, fuzzy
msgid "Confirm and exit"
-msgstr "Configurar e sair"
+msgstr "Confirmar e sair"
msgid "Add"
msgstr "Adicionar"
@@ -431,7 +429,6 @@ msgstr "Editar"
msgid "Delete"
msgstr "Eliminar"
-#, fuzzy
msgid "Select an action for '{}'"
msgstr "Selecione uma ação para '{}'"
@@ -449,27 +446,25 @@ msgstr ""
"Esta é a sua configuração escolhida:"
msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
-msgstr "Pacman já está a funcionar, a aguardar até 10 minutos para terminar."
+msgstr "O pacman já está a funcionar, a aguardar até 10 minutos para terminar."
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "Pacman lock não terminou. Por favor, limpe as sessões de pacman existentes antes de usar o archinstall."
+msgstr "O Pacman lock não terminou. Limpar as sessões de pacman existentes antes de usar o archinstall."
msgid "Choose which optional additional repositories to enable"
msgstr "Escolha quais repositórios adicionais opcionais a ativar"
-#, fuzzy
msgid "Add a user"
-msgstr "Adicionar um utilizador"
+msgstr "Adicionar utilizador"
-#, fuzzy
msgid "Change password"
-msgstr "Introduzir nova palavra-passe"
+msgstr "Alterar a palavra-passe"
msgid "Promote/Demote user"
msgstr "Promover/Demover utilizador"
msgid "Delete User"
-msgstr "Eliminar Utilizador"
+msgstr "Eliminar utilizador"
msgid ""
"\n"
@@ -479,25 +474,20 @@ msgstr ""
"Definir um novo utilizador\n"
msgid "User Name : "
-msgstr "Nome de Utilizador : "
+msgstr "Nome de utilizador : "
-#, fuzzy
msgid "Should {} be a superuser (sudoer)?"
-msgstr "Deve {} ser um superutilizador (sudoer)?"
+msgstr "{} deve ser um superutilizador (sudoer)?"
-#, fuzzy
msgid "Define users with sudo privilege: "
msgstr "Definir utilizadores com privilégio sudo: "
-#, fuzzy
msgid "No network configuration"
msgstr "Nenhuma configuração de rede"
-#, fuzzy
msgid "Set desired subvolumes on a btrfs partition"
msgstr "Definir subvolumes desejados numa partição btrfs"
-#, fuzzy
msgid ""
"{}\n"
"\n"
@@ -505,7 +495,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Selecte qual partição definir subvolumes"
+"Selecione em qual partição definir subvolumes"
msgid "Manage btrfs subvolumes for current partition"
msgstr "Gerir subvolumes btrfs para a partição atual"
@@ -519,16 +509,14 @@ msgstr "Guardar configuração de utilizador"
msgid "Save user credentials"
msgstr "Guardar credenciais de utilizador"
-#, fuzzy
msgid "Save disk layout"
-msgstr "Selecionar esquema de disco"
+msgstr "Guardar esquema de disco"
msgid "Save all"
msgstr "Guardar tudo"
-#, fuzzy
msgid "Choose which configuration to save"
-msgstr "Escolhe quais configurações guardar"
+msgstr "Escolha qual a configuração a guardar"
msgid "Enter a directory for the configuration(s) to be saved: "
msgstr "Introduz um diretório para as configurações a serem guardadas: "
@@ -539,13 +527,11 @@ msgstr "Não é uma diretoria válida: {}"
msgid "The password you are using seems to be weak,"
msgstr "A palavra-passe que estás a usar parece ser fraca,"
-#, fuzzy
msgid "are you sure you want to use it?"
-msgstr "tens a certeza que quer usar?"
+msgstr "tem certeza que deseja usá-la?"
-#, fuzzy
msgid "Optional repositories"
-msgstr "Repositórios adicionais"
+msgstr "Repositórios opcionais"
msgid "Save configuration"
msgstr "Guardar configuração"
@@ -554,15 +540,13 @@ msgid "Missing configurations:\n"
msgstr "Configurações em falta:\n"
msgid "Either root-password or at least 1 superuser must be specified"
-msgstr "É necessário especificar uma senha de root ou pelo menos 1 super-utilizador"
+msgstr "É necessário especificar uma palavra-passe de root ou pelo menos 1 super-utilizador"
-#, fuzzy
msgid "Manage superuser accounts: "
-msgstr "Gerir contas superutilizador: "
+msgstr "Administrar contas de superutilizador: "
-#, fuzzy
msgid "Manage ordinary user accounts: "
-msgstr "Gerir contas de utilizador: "
+msgstr "Administrar contas de utilizador normal: "
msgid " Subvolume :{:16}"
msgstr " Subvolume :{:16}"
@@ -595,341 +579,679 @@ msgstr "Guardar"
msgid "Subvolume name :"
msgstr "Nome do subvolume :"
-#, fuzzy
msgid "Select a mount point :"
msgstr "Selecione um ponto de montagem :"
msgid "Select the desired subvolume options "
msgstr "Selecione as opções desejadas do subvolume "
-#, fuzzy
msgid "Define users with sudo privilege, by username: "
-msgstr "Define utilizadores com privilégio sudo, por nome de utilizador: "
+msgstr "Defina utilizadores com privilégio sudo, por nome de utilizador: "
-#, fuzzy
msgid "[!] A log file has been created here: {}"
msgstr "[!] Um ficheiro de registo foi criado aqui: {}"
-#, fuzzy
msgid "Would you like to use BTRFS subvolumes with a default structure?"
-msgstr "Preferias usar o GRUB como bootloader ao invés de systemd-boot?"
+msgstr "Deseja usar subvolumes BTRFS com a estrutura predefinida?"
-#, fuzzy
msgid "Would you like to use BTRFS compression?"
-msgstr "Gostarias de usar swap em zram?"
+msgstr "Deseja usar a compressão BTRFS?"
-#, fuzzy
msgid "Would you like to create a separate partition for /home?"
-msgstr "Gostarias de usar swap em zram?"
+msgstr "Deseja criar uma partição separada para /home?"
msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
-msgstr ""
+msgstr "As unidades selecionadas não tem a capacidade mínima para sugestão automática\n"
msgid "Minimum capacity for /home partition: {}GB\n"
-msgstr ""
+msgstr "Capacidade mínima para partição /home : {}GB\n"
msgid "Minimum capacity for Arch Linux partition: {}GB"
-msgstr ""
+msgstr "Capacidade mínima para a partição do Arch Linux: {}GB"
msgid "Continue"
-msgstr ""
+msgstr "Continuar"
msgid "yes"
-msgstr ""
+msgstr "sim"
msgid "no"
-msgstr ""
+msgstr "não"
msgid "set: {}"
-msgstr ""
+msgstr "definir: {}"
msgid "Manual configuration setting must be a list"
-msgstr ""
+msgstr "O ajuste de configuração manual deve ser em lista"
-#, fuzzy
msgid "No iface specified for manual configuration"
-msgstr "Guardar configuração de utilizador"
+msgstr "Nenhum iface especificado para configuração manual"
msgid "Manual nic configuration with no auto DHCP requires an IP address"
-msgstr ""
+msgstr "A configuração manual de NIC sem DHCP automático requer um endereço IP"
msgid "Add interface"
-msgstr ""
+msgstr "Adicionar interface"
msgid "Edit interface"
-msgstr ""
+msgstr "Editar interface"
-#, fuzzy
msgid "Delete interface"
-msgstr "Eliminar Utilizador"
+msgstr "Deletar interface"
-#, fuzzy
msgid "Select interface to add"
-msgstr "Selecione uma interface de rede para configurar"
+msgstr "Selecione interface para adicionar"
-#, fuzzy
msgid "Manual configuration"
-msgstr "Guardar configuração"
+msgstr "Configuração manual"
-#, fuzzy
msgid "Mark/Unmark a partition as compressed (btrfs only)"
-msgstr "Marcar/Desmarcar uma partição como encriptada"
+msgstr "Marcar/desmarcar a partição como comprimida (apenas btrfs)"
-#, fuzzy
msgid "The password you are using seems to be weak, are you sure you want to use it?"
-msgstr "A palavra-passe que estás a usar parece ser fraca,"
+msgstr "A palavra-passe que está a usar parece ser fraca, tem a certeza que deseja utilizá-la?"
msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
-msgstr ""
+msgstr "Proporciona uma seleção de ambientes gráficos e gerenciadores de janela como por exemplo gnome, kde, sway"
msgid "Select your desired desktop environment"
-msgstr ""
+msgstr "Selecionar o ambiente gráfico desejado"
msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
-msgstr ""
+msgstr "Uma instalação bem básica que permite-lhe personalizar o Arch Linux como desejar."
msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
-msgstr ""
+msgstr "Proporciona uma seleção de diversos pacotes de servidor para instalar e ativar como por exemplo httpd, nginx, mariadb"
msgid "Choose which servers to install, if none then a minimal installation will be done"
-msgstr ""
+msgstr "Escolher os servidores a instalar, se não houver nenhum, será efetuada uma instalação mínima"
msgid "Installs a minimal system as well as xorg and graphics drivers."
-msgstr ""
+msgstr "Instala um sistema mínimo assim como xorg e controladores de vídeo."
msgid "Press Enter to continue."
-msgstr ""
+msgstr "Prima Enter para continuar."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr ""
+msgstr "Deseja fazer chroot para a nova instalação e realizar configuração pós-instalação?"
-#, fuzzy
msgid "Are you sure you want to reset this setting?"
-msgstr "tens a certeza que quer usar?"
+msgstr "Tem a certeza que desejar repor esta configuração?"
-#, fuzzy
msgid "Select one or more hard drives to use and configure\n"
-msgstr "Seleciona um ou mais discos rígidos para usar e configurar"
+msgstr "Selecionar uma ou mais unidades a usar e configurar\n"
msgid "Any modifications to the existing setting will reset the disk layout!"
-msgstr ""
+msgstr "Quaisquer modificações na configuração existente irá repor o esquema de disco!"
msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
-msgstr ""
+msgstr "Se repor a seleção da unidade isso também repor o esquema do disco atual. Tem a certeza?"
-#, fuzzy
msgid "Save and exit"
-msgstr "Configurar e sair"
+msgstr "Guardar e sair"
-#, fuzzy
msgid ""
"{}\n"
"contains queued partitions, this will remove those, are you sure?"
-msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?"
+msgstr ""
+"{}\n"
+"contém partições enfileiradas, isso irá removê-las, tem certeza?"
-#, fuzzy
msgid "No audio server"
-msgstr "Escolhe um servidor de áudio"
+msgstr "Sem servidor de áudio"
msgid "(default)"
-msgstr ""
+msgstr "(por omissão)"
-#, fuzzy
msgid "Use ESC to skip"
-msgstr ""
-"Usa ESC para saltar\n"
-"\n"
+msgstr "Use ESC para ignorar"
msgid ""
"Use CTRL+C to reset current selection\n"
"\n"
msgstr ""
+"Use CTRL+C para redefinir a seleção atual\n"
+"\n"
-#, fuzzy
msgid "Copy to: "
-msgstr "Copiar para :"
+msgstr "Copiar para: "
-#, fuzzy
msgid "Edit: "
-msgstr "Editar :"
+msgstr "Editar: "
msgid "Key: "
-msgstr ""
+msgstr "Chave: "
-#, fuzzy
msgid "Edit {}: "
-msgstr "Editar :"
+msgstr "Editar {}: "
msgid "Add: "
-msgstr ""
+msgstr "Adicionar: "
-#, fuzzy
msgid "Value: "
-msgstr "Valor :"
+msgstr "Valor: "
msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
-msgstr ""
+msgstr "Pode ignorar a seleção de unidade e particionar seja lá o que estiver montado em /mnt (experimental)"
msgid "Select one of the disks or skip and use /mnt as default"
-msgstr ""
+msgstr "Selecionar um dos discos ou ignorar e usar /mnt como predefinição"
-#, fuzzy
msgid "Select which partitions to mark for formatting:"
-msgstr ""
-"{}\n"
-"\n"
-"Seleciona a partição a mascar para formatar"
+msgstr "Selecionar quais partições a marcar para formatar:"
msgid "Use HSM to unlock encrypted drive"
-msgstr ""
+msgstr "Usar HSM para desbloquear unidade encriptada"
msgid "Device"
-msgstr ""
+msgstr "Dispositivo"
msgid "Size"
-msgstr ""
+msgstr "Tamanho"
msgid "Free space"
-msgstr ""
+msgstr "Espaço livre"
msgid "Bus-type"
-msgstr ""
+msgstr "Tipo de barramento"
-#, fuzzy
msgid "Either root-password or at least 1 user with sudo privileges must be specified"
-msgstr "É necessário especificar uma senha de root ou pelo menos 1 super-utilizador"
+msgstr "Deve-se especificar uma palavra-passe de root ou pelo menos 1 utilizador com privilégios de sudo"
-#, fuzzy
msgid "Enter username (leave blank to skip): "
-msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): "
+msgstr "Introduza um nome de utilizador (deixe em branco para ignorar): "
msgid "The username you entered is invalid. Try again"
-msgstr ""
+msgstr "O nome de utilizador que introduziu é inválido. Tente novamente"
-#, fuzzy
msgid "Should \"{}\" be a superuser (sudo)?"
-msgstr "Deve {} ser um superutilizador (sudoer)?"
+msgstr "\"{}\" deve ser um superutilizador (sudo)?"
-#, fuzzy
msgid "Select which partitions to encrypt"
-msgstr "Seleciona a partição a marcar como encriptada"
+msgstr "Selecionar quais as partições encriptar"
msgid "very weak"
-msgstr ""
+msgstr "muito fraca"
msgid "weak"
-msgstr ""
+msgstr "fraca"
msgid "moderate"
-msgstr ""
+msgstr "moderada"
msgid "strong"
-msgstr ""
+msgstr "forte"
-#, fuzzy
msgid "Add subvolume"
-msgstr " Subvolume :{:16}"
+msgstr "Adicionar subvolume"
msgid "Edit subvolume"
-msgstr ""
+msgstr "Editar subvolume"
-#, fuzzy
msgid "Delete subvolume"
-msgstr "Eliminar Utilizador"
+msgstr "Eliminar subvolume"
msgid "Configured {} interfaces"
-msgstr ""
+msgstr "{} interfaces configuradas"
msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr ""
+msgstr "Esta opção ativa o número de transferências paralelas que podem ocorrer durante a instalação"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
+"Inserir o número de transferências paralelas a ativar.\n"
+" (Inserir um valor entre 1 e {})\n"
+"Nota:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr ""
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Valor máximo : {} ( Permite {} transferências paralelas, permite {} transferências de cada vez )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr ""
+msgstr " - Valor mínimo : 1 ( Permite 1 transferência paralela, permite 2 transferências de cada vez )"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr ""
+msgstr " - Desativar/Padrão : 0 ( Desativa as transferências paralelas, permite apenas 1 transferência de cada vez )"
#, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr ""
+msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {max_downloads}, ou 0 para desativar]"
msgid "Parallel Downloads"
-msgstr ""
+msgstr "Descargas paralelas"
-#, fuzzy
msgid "ESC to skip"
-msgstr ""
-"Usa ESC para saltar\n"
-"\n"
+msgstr "ESC para sair"
msgid "CTRL+C to reset"
-msgstr ""
+msgstr "CTRL+C para reiniciar"
msgid "TAB to select"
-msgstr ""
+msgstr "TAB para selecionar"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[Valor predefinido: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Para poder usar esta tradução, instale manualmente um tipo de letra que suporte o idioma."
msgid "The font should be stored as {}"
-msgstr ""
+msgstr "O tipo de letra deve ser armazenado como {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "O Archinstall requer privilégios de root para ser executado. Consulte --help para mais informações."
+
+msgid "Select an execution mode"
+msgstr "Selecionar um modo de execução"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Não foi possível obter o perfil a partir do URL especificado: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Os perfis devem ter nomes únicos, mas foram encontradas definições de perfil com nomes duplicados: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Selecionar um ou mais dispositivos a usar e configurar"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Se repor a seleção do dispositivo, também irá repor o esquema atual do disco. Tem a certeza?"
+
+msgid "Existing Partitions"
+msgstr "Partições existentes"
+
+msgid "Select a partitioning option"
+msgstr "Selecionar uma opção de particionamento"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Inserir o diretório root dos dispositivos montados: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Capacidade mínima para partição /home : {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Capacidade mínima para a partição do Arch Linux: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo facilitar a instalação de ambientes gráficos"
+
+msgid "Current profile selection"
+msgstr "Seleção de perfil atual"
+
+msgid "Remove all newly added partitions"
+msgstr "Remover todas as partições recém adicionadas"
+
+msgid "Assign mountpoint"
+msgstr "Atribuir um ponto de montagem"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Marcar/Desmarcar para ser formatada (apaga os dados)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Marcar/Desmarcar como partição de aranque"
+
+msgid "Change filesystem"
+msgstr "Alterar sistema de ficheiros"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Marcar/Desmarcar como comprimida"
+
+msgid "Set subvolumes"
+msgstr "Definir subvolumes"
+
+msgid "Delete partition"
+msgstr "Eliminar partição"
+
+msgid "Partition"
+msgstr "Partição"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Esta partição está encriptada. Para formatá-la, deve ser especificado um sistema de ficheiros"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Se o ponto de montagem /boot for definido, a partição também será marcada como inicializável."
+
+msgid "Mountpoint: "
+msgstr "Ponto de montagem: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Atuais setores livres no dispositivo {}:"
+
+msgid "Total sectors: {}"
+msgstr "Total de setores: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Inserir o setor inicial (predefinido: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Inserir o setor final da partição (percentagem ou número de bloco, predefinido: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Isto irá remover todas as partições recém adicionadas, continuar?"
+
+msgid "Partition management: {}"
+msgstr "Gestão de partições: {}"
+
+msgid "Total length: {}"
+msgstr "Tamanho total: {}"
-#, fuzzy
msgid "Encryption type"
-msgstr "Define a palavra-passe de encriptação"
+msgstr "Tipo de encriptação"
msgid "Partitions"
-msgstr ""
+msgstr "Partições"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Nenhum dispositivo HSM disponível"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Seleciona a partição a marcar como encriptada"
+msgstr "Partições a serem encriptadas"
-#, fuzzy
msgid "Select disk encryption option"
-msgstr "Seleciona o esquema de disco"
+msgstr "Selecionar a opção de encriptação de disco"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Selecionar um dispositivo FIDO2 para usar como HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Usar um esquema de partições predefinido de melhor desempenho"
+
+msgid "Manual Partitioning"
+msgstr "Particionamento manual"
+
+msgid "Pre-mounted configuration"
+msgstr "Configuração pré-montada"
+
+msgid "Unknown"
+msgstr "Desconhecido"
+
+msgid "Partition encryption"
+msgstr "Encriptação de partições"
+
+msgid " ! Formatting {} in "
+msgstr " ! A formatar {} em "
+
+msgid "↠Back"
+msgstr "↠Voltar"
+
+msgid "Disk encryption"
+msgstr "Encriptação de discos"
+
+msgid "Configuration"
+msgstr "Configuração"
+
+msgid "Password"
+msgstr "Palavra-passe"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?"
+msgstr "Todas as definições serão repostas, tem a certeza?"
msgid "Back"
+msgstr "Voltar"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Escolha qual a interface gráfica de início de sessão a instalar para os perfis escolhidos: {}"
+
+msgid "Environment type: {}"
+msgstr "Tipo de ambiente: {}"
+
+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 "O controlador proprietário Nvidia não é suportado pelo Sway. É provável que encontre problemas. Está de acordo com isso?"
+
+msgid "Installed packages"
+msgstr "Pacotes instalados"
+
+msgid "Add profile"
+msgstr "Adicionar perfil"
+
+msgid "Edit profile"
+msgstr "Editar perfil"
+
+msgid "Delete profile"
+msgstr "Eliminar perfil"
+
+msgid "Profile name: "
+msgstr "Nome do perfil: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "O nome do perfil que introduziu já está a ser utilizado. Tente novamente"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Pacotes a serem instalados com este perfil (separados por espaço, deixe em branco para ignorar): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Serviços a serem ativados com este perfil (separados por espaço, deixe em branco para ignorar): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Este perfil deve ser ativado para instalação?"
+
+msgid "Create your own"
+msgstr "Criar o seu próprio"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"\n"
+"Selecionar um controlador gráfico ou deixe em branco para instalar todos os controladores de código aberto"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "O Sway precisa de acesso ao seu \"seat\" (conjunto de dispositivos de hardware, como o teclado, o rato, etc)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Selecionar uma opção para permitir o acesso do Sway ao seu hardware"
-#, fuzzy
-msgid "Password"
-msgstr "Palavra-passe de root"
+msgid "Graphics driver"
+msgstr "Controlador gráfico"
-msgid "Partition encryption"
+msgid "Greeter"
+msgstr "Interface gráfica de início de sessão"
+
+msgid "Please chose which greeter to install"
+msgstr "Escolha qual a interface gráfica de início de sessão a instalar"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Esta é uma lista de perfis pré-programados (default_profiles)"
+
+msgid "Disk configuration"
+msgstr "Configuração do disco"
+
+msgid "Profiles"
+msgstr "Perfis"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "A procurar possíveis diretórios para guardar os ficheiros de configuração ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Selecionar um ou mais diretórios para guardar ficheiros de configuração"
+
+msgid "Add a custom mirror"
+msgstr "Adicionar um espelho personalizado"
+
+msgid "Change custom mirror"
+msgstr "Alterar espelho personalizado"
+
+msgid "Delete custom mirror"
+msgstr "Eliminar espelho personalizado"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Inserir o nome (deixe em branco para ignorar): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Insira o url (deixe em branco para ignorar): "
+
+msgid "Select signature check option"
+msgstr "Selecionar a opção de verificação da assinatura"
+
+msgid "Select signature option"
+msgstr "Selecionar a opção de assinatura"
+
+msgid "Custom mirrors"
+msgstr "Espelhos personalizados"
+
+msgid "Defined"
+msgstr "Definido"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Guardar configuração de utilizador (incluindo esquema do disco)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Insira um diretório para a(s) configuração(ões) serem guardadas (conclusão de tab ativada): \n"
+"Guardar diretório: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Pretende guardar o(s) ficheiro(s) de configuração de {} na seguinte localização?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "A guardar ficheiros de configuração de {} para {}"
+
+msgid "Mirrors"
+msgstr "Espelhos"
+
+msgid "Mirror regions"
+msgstr "Regiões dos espelhos"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Valor máximo : {} ( Permite {} transferências paralelas, permite {max_downloads+1} transferências de cada vez )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]"
+
+msgid "Locales"
+msgstr "Localidades"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Usar o Gestor de Redes \"NetworkManager\" (necessário para configurar a Internet graficamente no GNOME e KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Total: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Todos os valores inseridos podem ser sufixados com uma unidade: B, KB, KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Se nenhuma unidade for fornecida, o valor será interpretado como setores"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Inserir o início (predefinido: sector {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Inserir o fim (predefinido: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Não foi possível determinar os dispositivos fido2. A libfido2 está instalada?"
+
+msgid "Path"
+msgstr "Caminho"
+
+msgid "Manufacturer"
+msgstr "Fabricante"
+
+msgid "Product"
+msgstr "Produto"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configuração inválida: {error}"
+
+msgid "Type"
+msgstr "Tipo"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Esta opção ativa o número de transferências paralelas que podem ocorrer durante as transferências de pacotes"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Inserir o número de transferências paralelas a ativar.\n"
+"\n"
+"Nota:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Valor máximo recomendado : {} ( Permite {} transferências paralelas de cada vez )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Desativar/Predefinido : 0 ( Desativa as transferências paralelas, permite apenas 1 transferência de cada vez )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Entrada inválida! Tente novamente com uma entrada válida [ou 0 para desativar]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "O Hyprland precisa de acesso ao seu \"seat\" (conjunto de dispositivos de hardware, como o teclado, o rato, etc)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Selecionar uma opção para permitir o acesso do Hyprland ao seu hardware"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Todos os valores inseridos podem ser sufixados com uma unidade: %, B, KB, KiB, MB, MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Gostaria de usar imagens de kernel unificados?"
+
+msgid "Unified kernel images"
+msgstr "Imagens de kernel unificados"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "À espera que a sincronização da hora (timedatectl show) seja concluída."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "A sincronização da hora não está a ser concluída, enquanto espera - consulte a documentação para obter soluções alternativas: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Ignorar a espera pela sincronização automática da hora (isto pode causar problemas se a hora estiver dessincronizada durante a instalação)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "A aguardar que a sincronização do chaveiro do Arch Linux (archlinux-keyring-wkd-sync) seja concluída."
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Eliminar perfil"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Escreve o sector de início (percentagem ou número de bloco, padrão: {}): "
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "A sincronização da hora não está a ser concluída, enquanto espera - consulte a documentação para obter soluções alternativas: https://archinstall.readthedocs.io/"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Escreve o sector final da partição (percentagem ou número de bloco, ex: {}): "
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Marcar/Desmarcar como partição de aranque"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Deseja usar a compressão BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
#~ msgid "Add :"
#~ msgstr "Adicionar :"
diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo
index bcffafbf..4714d73b 100644
--- a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo
+++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po
index 33e78a20..2910805e 100644
--- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po
+++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po
@@ -1,13 +1,23 @@
# Translators:
# @Cain-dev (cain-dev.github.io)
# Rafael Fontenelle <rafaelff@gnome.org>
+# Jefferson Michael <github.com/jeffersonjpr>
+# Diogo Silva <github.com/Diogo-ss>
+# Mário Victor Ribeiro Silva <github.com/ComicShrimp>
+# Rafael Fontenelle <rafaelff@gnome.org>, 2023.
msgid ""
msgstr ""
+"Project-Id-Version: archinstall\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2023-12-05 13:30-0300\n"
"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Language-Team: \n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Gtranslator 45.2\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] Um arquivo de log foi criado aqui: {} {}"
@@ -60,7 +70,7 @@ msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em
msgid "Copy ISO network configuration to installation"
msgstr "Copiar a configuração de rede da ISO para a instalação"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)"
msgid "Select one network interface to configure"
@@ -95,10 +105,10 @@ msgid "Enter a desired filesystem type for the partition"
msgstr "Digite o tipo de sistema de arquivos desejado para a partição"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Digite o local inicial (em unidades do parted: s, GB, %, etc. ; padrão: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Digite o local final (em unidades do parted: s, GB, %, etc. ; padrão: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?"
@@ -619,7 +629,7 @@ msgid "Manual configuration setting must be a list"
msgstr "O ajuste de configuração manual deve ser em lista"
msgid "No iface specified for manual configuration"
-msgstr "iface não especificada para configuração manual"
+msgstr "Nenhum iface especificado para configuração manual"
msgid "Manual nic configuration with no auto DHCP requires an IP address"
msgstr "A configuração manual de NIC sem DHCP automático requer um endereço IP"
@@ -791,21 +801,20 @@ msgstr "{} interfaces configuradas"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Esta opção habilita o número de downloads paralelos que podem ocorrer durante a instalação"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Insira o número de downloads paralelos para serem habilitados.\n"
-" (Insira um valor entre 1 e {max_downloads})\n"
+" (Insira um valor entre 1 e {})\n"
"Observação:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - Valor máximo : {max_downloads} ( Permite {max_donwloads} downloads paralelos, permite {max_donwloads+1} downloads por vez )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} downloads por vez )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr " - Valor minimo : 1 ( Permite 1 download paralelo, permite 2 downloads por vez )"
+msgstr " - Valor mínimo : 1 ( Permite 1 download paralelo, permite 2 downloads por vez )"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apenas 1 download por vez )"
@@ -817,9 +826,8 @@ msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {max_
msgid "Parallel Downloads"
msgstr "Downloads Paralelos"
-#, fuzzy
msgid "ESC to skip"
-msgstr "Use ESC para pular"
+msgstr "ESC para sair"
msgid "CTRL+C to reset"
msgstr "CTRL+C para reiniciar"
@@ -836,45 +844,418 @@ msgstr "Para poder usar esta tradução, instale manualmente uma fonte que supor
msgid "The font should be stored as {}"
msgstr "A fonte deve ser armazenada como {}"
-#, fuzzy
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "O Archinstall requer privilégios de root para ser executado. Consulte --help para mais informações."
+
+msgid "Select an execution mode"
+msgstr "Selecione um modo de execução"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Não foi possível obter o perfil a partir da URL especificada: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Os perfis devem ter nomes únicos, mas foram encontradas definições de perfil com nomes duplicados: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Selecione um ou mais dispositivos para usar e configurar"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Se você redefinir a seleção de dispositivo isso também redefinirá o layout do dispositivo atual. Tem certeza?"
+
+msgid "Existing Partitions"
+msgstr "Partições existentes"
+
+msgid "Select a partitioning option"
+msgstr "Selecione uma opção de particionamento"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Digite o diretório raiz dos dispositivos montados: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Capacidade mínima para partição /home : {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Capacidade mínima para a partição do Arch Linux: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo facilitar a instalação de ambientes gráficos"
+
+msgid "Current profile selection"
+msgstr "Seleção de perfil atual"
+
+msgid "Remove all newly added partitions"
+msgstr "Remova todas as partições recém-adicionadas"
+
+msgid "Assign mountpoint"
+msgstr "Atribuir um ponto de montagem"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Marcar/Desmarcar para ser formatada (apaga os dados)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Marcar/desmarcar como inicializável"
+
+msgid "Change filesystem"
+msgstr "Mudar arquivo do sistema"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Marcar/desmarcar como comprimida (apenas btrfs)"
+
+msgid "Set subvolumes"
+msgstr "Definir subvolumes"
+
+msgid "Delete partition"
+msgstr "Deletar partição"
+
+msgid "Partition"
+msgstr "Partição"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Esta partição está criptografada. Para formatá-la, um sistema de arquivos deve ser especificado"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Se o ponto de montagem /boot for definido, a partição também será marcada como inicializável."
+
+msgid "Mountpoint: "
+msgstr "Ponto de montagem: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Setores livres no dispositivo {}:"
+
+msgid "Total sectors: {}"
+msgstr "Total de setores: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Digite o setor de início (padrão: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Digite o setor final da partição (porcentagem ou número de bloco, padrão: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Isso irá remover todas as partições recém-adicionadas, continuar?"
+
+msgid "Partition management: {}"
+msgstr "Gerenciamento de partições: {}"
+
+msgid "Total length: {}"
+msgstr "Tamanho total: {}"
+
msgid "Encryption type"
-msgstr "Senha de encriptação"
+msgstr "Tipo de encriptação"
msgid "Partitions"
-msgstr ""
+msgstr "Partições"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Nenhum dispositivo HSM disponível"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Selecione quais partições encriptar"
+msgstr "Partições a serem encriptadas"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "Selecione a opção de encriptação de disco"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Selecione um dispositivo FIDO2 para usar como HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Usar um esquema de partições padrão de melhor desempenho"
+
+msgid "Manual Partitioning"
+msgstr "Particionamento manual"
+
+msgid "Pre-mounted configuration"
+msgstr "Configuração pré-montada"
+
+msgid "Unknown"
+msgstr "Desconhecido"
+
+msgid "Partition encryption"
+msgstr "Encriptação de partição"
+
+msgid " ! Formatting {} in "
+msgstr " ! Formatando {} em "
+
+msgid "↠Back"
+msgstr "↠Voltar"
+
+msgid "Disk encryption"
+msgstr "Encriptação de disco"
+
+msgid "Configuration"
+msgstr "Configuração"
+
+msgid "Password"
+msgstr "Senha"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?"
+msgstr "Todas as configurações serão redefinidas, tem certeza?"
msgid "Back"
+msgstr "Voltar"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Por favor, escolha qual greeter instalar para os perfis escolhidos: {}"
+
+msgid "Environment type: {}"
+msgstr "Tipo de ambiente: {}"
+
+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 "O driver proprietário Nvidia não é suportado pelo Sway. É provável que você encontre problemas. Você está de acordo com isso?"
+
+msgid "Installed packages"
+msgstr "Pacotes instalados"
+
+msgid "Add profile"
+msgstr "Adicionar perfil"
+
+msgid "Edit profile"
+msgstr "Editar perfil"
+
+msgid "Delete profile"
+msgstr "Deletar perfil"
+
+msgid "Profile name: "
+msgstr "Nome do perfil: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "O nome do perfil que você digitou já esta em uso. Tente novamente"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Pacotes a serem instalados com este perfil (separados por espaço, deixe em branco para pular): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Serviços a serem ativados com este perfil (separados por espaço, deixe em branco para pular): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Este perfil deve ser ativado para instalação?"
+
+msgid "Create your own"
+msgstr "Crie o seu próprio"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"\n"
+"Selecione um driver gráfico ou deixe em branco para instalar todos os drivers de código aberto"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "O Sway precisa de acesso ao seu seat (conjunto de dispositivos de hardware, como teclado, mouse, etc)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Selecione uma opção para permitir o acesso do Sway ao seu hardware"
+
+msgid "Graphics driver"
+msgstr "Driver gráfico"
+
+msgid "Greeter"
+msgstr "Greeter"
+
+msgid "Please chose which greeter to install"
+msgstr "Por favor, escolha qual greeter instalar"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Esta é uma lista de perfis pré-programados (default_profiles)"
+
+msgid "Disk configuration"
+msgstr "Configuração do disco"
+
+msgid "Profiles"
+msgstr "Perfis"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Procurando possiveis diretórios para salvar os arquivos de configuração ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Selecione um ou mais diretórios para salvar arquivos de configuração"
+
+msgid "Add a custom mirror"
+msgstr "Adicionar mirror personalizado"
+
+msgid "Change custom mirror"
+msgstr "Alterar mirror personalizado"
+
+msgid "Delete custom mirror"
+msgstr "Excluir mirror personalizado"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Digite o nome (deixe em branco para pular): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Digite a url (deixe em branco para pular): "
+
+msgid "Select signature check option"
+msgstr "Selecione uma opção de verificação de assinatura"
+
+msgid "Select signature option"
+msgstr "Selecione uma opção de assinatura"
+
+msgid "Custom mirrors"
+msgstr "Mirrors personalizados"
+
+msgid "Defined"
+msgstr "Definido"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Salvar configuração de usuário (incluindo layout do disco)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Digite um diretório para as configurações serem salvas (completamento de tab ativado): \n"
+"Salvar diretório: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Você deseja salvar arquivo(s) de configuração de {} nos locais a seguir?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Salvando arquivos de configuração de {} para {}"
+
+msgid "Mirrors"
+msgstr "Mirrors"
+
+msgid "Mirror regions"
+msgstr "Regiões dos mirrors"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {max_downloads+1} downloads por vez )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]"
+
+msgid "Locales"
+msgstr "Localidades"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Total: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Todos os valores inseridos podem ser seguidos por uma unidade: B, KB, KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Se nenhuma unidade for fornecida, o valor será interpretado como setores"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Digite o início (padrão: setor {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Digite fim (padrão: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Incapaz de determinar dispositivos FIDO2. O libfido2 está instalado?"
+
+msgid "Path"
+msgstr "Caminho"
+
+msgid "Manufacturer"
+msgstr "Fabricante"
+
+msgid "Product"
+msgstr "Produto"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configuração inválida: {error}"
+
+msgid "Type"
+msgstr "Tipo"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Esta opção habilita o número de downloads paralelos que podem ocorrer durante os downloads de pacotes"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Insira o número de downloads paralelos para serem habilitados.\n"
+"\n"
+"Observação:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Valor máximo recomendado : {} ( Permite {} downloads paralelos por vez )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apenas 1 download por vez )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Entrada inválida! Tente novamente com uma entrada válida [ou 0 para desativar]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "O Hyprland precisa de acesso ao seu seat (conjunto de dispositivos de hardware, como teclado, mouse, etc)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Selecione uma opção para permitir o acesso do Hyprland ao seu hardware"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Todos os valores inseridos podem ser seguidos por uma unidade: B, KB, KiB, MB, MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Deseja usar imagens de kernel unificados?"
+
+msgid "Unified kernel images"
+msgstr "Imagens de kernel unificados"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Aguardando a sincronização do fuso horário (timedatectl show) ser concluida."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Sincronização de fuso horário não concluída, enquanto você espera - confira a documentação para soluções alternativas: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Pulando a espera pela sincronização automática de fuso horário (isso pode causar problemas se o fuso horário estiver desincronizado durante a instalação)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Aguardando a sincronização do Arch Linux Keyring (archlinux-keyring-wkd-sync) ser concluída."
#, fuzzy
-msgid "Password"
-msgstr "Senha de root"
+msgid "Selected profiles: "
+msgstr "Deletar perfil"
-msgid "Partition encryption"
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Sincronização de fuso horário não concluída, enquanto você espera - confira a documentação para soluções alternativas: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Marcar/desmarcar como inicializável"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Deseja usar a compressão BTRFS?"
+
+msgid "Use compression"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Digite o setor de início (porcentagem ou número do bloco, padrão: {}): "
+msgid "Disable Copy-on-Write"
+msgstr ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Digite o setor final da partição (porcentagem ou número de bloco, ex.: {}): "
+#~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: "
+#~ msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: "
diff --git a/archinstall/locales/ro/LC_MESSAGES/base.mo b/archinstall/locales/ro/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..8be1d632
--- /dev/null
+++ b/archinstall/locales/ro/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ro/LC_MESSAGES/base.po b/archinstall/locales/ro/LC_MESSAGES/base.po
new file mode 100644
index 00000000..be6f0467
--- /dev/null
+++ b/archinstall/locales/ro/LC_MESSAGES/base.po
@@ -0,0 +1,1252 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: andrewKode iancu.andrei312@gmail.com\n"
+"Language-Team: \n"
+"Language: ro\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.3.1\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] Un fișier jurnal a fost creat aici: {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " Vă rog să raportați problema (și fișierul) la https://github.com/archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "Sunteți sigur(ă) că doriți să închideți?"
+
+msgid "And one more time for verification: "
+msgstr "Încă odată pentru verificare: "
+
+msgid "Would you like to use swap on zram?"
+msgstr "Doriți să folosiți swap pe zram?"
+
+msgid "Desired hostname for the installation: "
+msgstr "Hostname-ul dorit pentru instalare: "
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "Nume utilizator necesar pentru utilizatorul cu drepturi privilegiate: "
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "Alți utilizatori pentru adăugare (lăsați gol pentru niciunul): "
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "Acest utilizator ar trebui să aibă drepturi privilegiate (sudoer)?"
+
+msgid "Select a timezone"
+msgstr "Selectați un fus orar"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "Doriți să folosiți GRUB ca bootloader în locul systemd-boot?"
+
+msgid "Choose a bootloader"
+msgstr "Selectați un bootloader"
+
+msgid "Choose an audio server"
+msgstr "Selectați un server audio"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "Doar pachetele precum base, base-devel, linux, linux-firmware, egibootmgr și cele opționale de profil sunt instalate."
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr "Dacă doriți un browser, precum firefox sau chromium, puteți să-l specificați în promptul următor."
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "Scrieți pachete adiționale pe care doriți să le instalați(separare prin spațiu, lăsați liber pentru a ignora): "
+
+msgid "Copy ISO network configuration to installation"
+msgstr "Copiați configurarea ISO prin rețea la instalare"
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "Folosiți NetworkManager (necesar pentru configurarea setărilor de rețea într-un mod grafic pentru GNOME și KDE)"
+
+msgid "Select one network interface to configure"
+msgstr "Selectați o interfață de rețea pentru configurare"
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr "Selectați modul de configurare pentru \"{}\" sau folosiți modul predefinit \"{}\""
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "Introduceți IP-ul și subrețeaua pentru {} (example: 192.168.0.5/24): "
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "Introduceți IP-ul gateway-ului (router) sau lăsați liber pentru niciunul: "
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr "Introduceți serverele dumneavoastră DNS (separare prin spațiu, lăsați liber pentru a ignora): "
+
+msgid "Select which filesystem your main partition should use"
+msgstr "Selectați sistemul de fișiere dorit pentru partiția dumneavoastră primară"
+
+msgid "Current partition layout"
+msgstr "Schema de partiționare curentă"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"Selectați ce doriți să se întâmple cu\n"
+"{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "Introduceți un sistem de fișiere dorit pentru partiția"
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "Introduceți sectorul de început (procentaj sau numărul block-ului, predefinit: {}): "
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "Introduceți locația finală (în unități de partiționare: s, GB, %, etc. ; ex: {}): "
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "{} conține partiții în așteptare, acest proces le va șterge, doriți acest lucru?"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați pe baza indexului partițiile pentru ștergere"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați pe baza indexului partițiile pentru a fi montate și locația"
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Punctele de montare ale partițiilor sunt relevante pentru instalate, de exemplu partiția boot va fi /boot."
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr "Selectați locul de montare a partiției (lăsați gol pentru a șterge punctul de montare): "
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați partiția care urmează să fie marcată pentru formatare"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați partiția care urmează să fie marcată ca fiind cripitată"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați partiția care urmează să fie marcată ca fiind bootabilă"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați partiția pentru care urmează să fie setat un sistem de fișiere"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "Introduceți un sistem de fișiere dorit pentru partiție: "
+
+msgid "Archinstall language"
+msgstr "Limba pentru Archinstall"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr "Șterge toate discurile selectate și folosește cea mai bună schemă de partiționare"
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr "Selectați ce doriți să se întâmple cu fiecare disc în parte (urmat de capacitatea utilizată dorită)"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "Selectați ce doriți să faceți cu dispozitivele de tip bloc selectate"
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr "Aceasta este o listă de profiluri preprogramate, ar putea să ușureze instalarea unor lucruri precum spațiile de lucru"
+
+msgid "Select keyboard layout"
+msgstr "Selectați aranjamentul tastaturii"
+
+msgid "Select one of the regions to download packages from"
+msgstr "Selectați regiunea pentru care se vor descărca pachetele"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "Selectați unul sau mai multe discuri dure pentru a fi utilizate și configurate"
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr "Pentru cea mai bună compatibilitate cu hardware-ul dumneavoastră AMD ați putea folosi fie opțiunea All Open-Source sau 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 "Pentru cea mai bună compatibilitate cu hardware-ul dumneavoastră Intel ați putea folosi fie opțiunea All Open-Source sau Intel.\n"
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr "Pentru cea mai bună compatibilitate cu hardware-ul dumneavoastră Nvidia ați putea folosi opțiunea Nvidia proprietary driver.\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Selectați un driver grafic sau lăsați liber pentru a instala toate driverele open-source"
+
+msgid "All open-source (default)"
+msgstr "Toate open-source (predefinit)"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "Alegeți nucelii care urmează să fie folosiți sau lăsați liber pentru valorarea predefinită \"{}\""
+
+msgid "Choose which locale language to use"
+msgstr "Alegeți limba locală pentru utilizare"
+
+msgid "Choose which locale encoding to use"
+msgstr "Alegeți codificarea locală pentru utilizare"
+
+msgid "Select one of the values shown below: "
+msgstr "Selecați o valoare din cele afișate mai jos: "
+
+msgid "Select one or more of the options below: "
+msgstr "Selecați una sau mai multe opțiuni din cele afișate mai jos: "
+
+msgid "Adding partition...."
+msgstr "Adaug partiția...."
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr "Trebuie să introduceți un tip de fișiere de sistem valid pentru a continua. Vedeți 'man parted' pentru tipurile de fișiere de sistem disponibile."
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "Eroare: Listare profiluri pe URL \"{}\" rezultate în:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "Eroare: Nu s-a putut decoda rezultatul \"{}\" ca un JSON:"
+
+msgid "Keyboard layout"
+msgstr "Aranjament tastatură"
+
+msgid "Mirror region"
+msgstr "Regiune de oglindire"
+
+msgid "Locale language"
+msgstr "Limba locală"
+
+msgid "Locale encoding"
+msgstr "Codarea locală"
+
+msgid "Drive(s)"
+msgstr "Disc(uri)"
+
+msgid "Disk layout"
+msgstr "Schema discului"
+
+msgid "Encryption password"
+msgstr "Parola de criptare"
+
+msgid "Swap"
+msgstr "Swap"
+
+msgid "Bootloader"
+msgstr "Bootloader"
+
+msgid "Root password"
+msgstr "Parola pentru root"
+
+msgid "Superuser account"
+msgstr "Cont utilizator privilegiat (root)"
+
+msgid "User account"
+msgstr "Cont utilizator"
+
+msgid "Profile"
+msgstr "Profil"
+
+msgid "Audio"
+msgstr "Audio"
+
+msgid "Kernels"
+msgstr "Nuclei"
+
+msgid "Additional packages"
+msgstr "Pachete Adiționale"
+
+msgid "Network configuration"
+msgstr "Configurarea rețelei"
+
+msgid "Automatic time sync (NTP)"
+msgstr "Timpul de sincronizare automate (NTP)"
+
+msgid "Install ({} config(s) missing)"
+msgstr "Instalalare ({} lipsă configurație(ii))"
+
+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 ""
+"Ați decis să treceți peste selectarea discurilor dure\n"
+"și se va folosi orice configurație de disc montată la {} (experimental)\n"
+"ATENȚIE: Archinstall nu va verifica compatibilitatea configurării\n"
+"Doriți să continuați?"
+
+msgid "Re-using partition instance: {}"
+msgstr "Reutilizez instanța partiției: {}"
+
+msgid "Create a new partition"
+msgstr "Crează o partiție nouă"
+
+msgid "Delete a partition"
+msgstr "Șterge o partiție"
+
+msgid "Clear/Delete all partitions"
+msgstr "Curăță/Șterge toate partițile"
+
+msgid "Assign mount-point for a partition"
+msgstr "Asignează un punct de montare pentru o partiție"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "Marchează/Demarchează o partiție pentru a fi formatată (șterge toate datele)"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "Marchează/Demarchează o partiție pentru a fi criptată"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "Marchează/Demarchează o partiție ca fiind bootabilă (automat pentru /boot)"
+
+msgid "Set desired filesystem for a partition"
+msgstr "Setează sistemul de fișiere dorit pentru o partiție"
+
+msgid "Abort"
+msgstr "Anulează"
+
+msgid "Hostname"
+msgstr "Hostname"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "Nu este configurat, indisponibil, doar configurat manual"
+
+msgid "Timezone"
+msgstr "Fus orar"
+
+msgid "Set/Modify the below options"
+msgstr "Setează/Modifică opțiunile de mai jos"
+
+msgid "Install"
+msgstr "Instalează"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"Folosiți ESC pentru a ignora\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "Sugerează o schemă de partiționare"
+
+msgid "Enter a password: "
+msgstr "Introduceți o parolă: "
+
+msgid "Enter a encryption password for {}"
+msgstr "Introduceți o parolă de criptare pentru {}"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "Introduceți parola de criptare a discului (lăsați liber pentru a evita criptarea): "
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "Crează un utilizator cu privilegii (root): "
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "Introuceți parola pentru root (lăsați liber pentru a dezactiva utilizatorul root): "
+
+msgid "Password for user \"{}\": "
+msgstr "Parola pentru utilizatorul \"{}\": "
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr "Verific dacă pachetele adiționale există (acest proces ar putea dura câteva secunde)"
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr "Doriți să utilizați sincronizarea timpului automat (NTP) cu serverele de timp predefinite?\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 ""
+"Timpul pentru hardware alți pași după configurare ar putea fi necesari pentru ca NTP să funcționeze.\n"
+"Pentru mai multe informații verificații documentația Arch (Arch Wiki)"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "Introduceți un nume utilizator pentru a crea un user adițional (lăsați liber pentru a ignora): "
+
+msgid "Use ESC to skip\n"
+msgstr "Foloți ESC pentru a ignora\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+"\n"
+" Alegeți un obiect din listă și selectați o acțiune disponibilă pentru a fi executată"
+
+msgid "Cancel"
+msgstr "Opriți"
+
+msgid "Confirm and exit"
+msgstr "Confirmă și ieși"
+
+msgid "Add"
+msgstr "Adaugă"
+
+msgid "Copy"
+msgstr "Copiază"
+
+msgid "Edit"
+msgstr "Editează"
+
+msgid "Delete"
+msgstr "Șterge"
+
+msgid "Select an action for '{}'"
+msgstr "Selectează o acțiune pentru '{}'"
+
+msgid "Copy to new key:"
+msgstr "Copiază la noua cheie:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "Tip necunoscut: {}. Valorile acceptate sunt {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"Aceasta este configurația selectată de către dumneavoastră:"
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "Pacman rulează deja, aștept maxim 10 minute pentru a termina."
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr "Procesul pacman anterior nu s-a terminat corect. Vă rog, ștergeți orice sesiune de pacman existentă înainte de a continuea cu archinstall."
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "Alegeți ce colecție opțională de pachete să fie activată"
+
+msgid "Add a user"
+msgstr "Adaugă un utilizator"
+
+msgid "Change password"
+msgstr "Schimbă parola"
+
+msgid "Promote/Demote user"
+msgstr "Promovează/Retrogradează un utilizator"
+
+msgid "Delete User"
+msgstr "Șterge utilizatorul"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"Definește un utilizator nou\n"
+
+msgid "User Name : "
+msgstr "Nume utilizator : "
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "Ar trebui ca {} să fie un utilizator cu privilegii (sudoer)?"
+
+msgid "Define users with sudo privilege: "
+msgstr "Definiți utilizatorii cu privilegii sudo: "
+
+msgid "No network configuration"
+msgstr "Nu există o configurare de rețea"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "Setați subvolumele dorite pe o partiție btrfs"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}\n"
+"\n"
+"Selectați partiția pentru care se vor seta subvolumele"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "Administrează subvolumele btrfs pentru partiția curentă"
+
+msgid "No configuration"
+msgstr "Nu există configurație"
+
+msgid "Save user configuration"
+msgstr "Salvează configurația utilizatorului"
+
+msgid "Save user credentials"
+msgstr "Salvează credențialele utilizatorului"
+
+msgid "Save disk layout"
+msgstr "Salvează schema discului"
+
+msgid "Save all"
+msgstr "Salvați tot"
+
+msgid "Choose which configuration to save"
+msgstr "Alegeți configurația dorită pentru salvare"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "Introduceți un director pentru salvarea configurațiilor: "
+
+msgid "Not a valid directory: {}"
+msgstr "Nu este un director valid: {}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "Parola pe care doriți să o folosiți pare să fie slabă,"
+
+msgid "are you sure you want to use it?"
+msgstr "sigur doriți să o folosiți?"
+
+msgid "Optional repositories"
+msgstr "Colecții de pachete opționale"
+
+msgid "Save configuration"
+msgstr "Salvează configurația"
+
+msgid "Missing configurations:\n"
+msgstr "Configurații lipsă:\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "Este necesar ca parola pentru utilizatorul root sau măcar un utilizator cu privilegii să fie adăugat"
+
+msgid "Manage superuser accounts: "
+msgstr "Administrează conturile pentru utilizatorii cu privilegii: "
+
+msgid "Manage ordinary user accounts: "
+msgstr "Adminstrează conturile pentru utilizatorii obișnuiți: "
+
+msgid " Subvolume :{:16}"
+msgstr " Subvolum :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " montat la {:16}"
+
+msgid " with option {}"
+msgstr " cu opțiunea {}"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+" Completați valorile dorite pentru un subvolum nou \n"
+
+msgid "Subvolume name "
+msgstr "Nume subvolum "
+
+msgid "Subvolume mountpoint"
+msgstr "Punct de montare subvolum"
+
+msgid "Subvolume options"
+msgstr "Opțiuni subvolum"
+
+msgid "Save"
+msgstr "Salvează"
+
+msgid "Subvolume name :"
+msgstr "Nume subvolum :"
+
+msgid "Select a mount point :"
+msgstr "Seleactați un punct de montare :"
+
+msgid "Select the desired subvolume options "
+msgstr "Selectați opțiunile dorite pentru subvolum "
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "Definiți utilizatorii cu privilegii sudo, după nume utilizator: "
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] Un fișier de jurnal a fost creat aici: {}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "Doriți să folosiți subvolume BTRFS cu o structură predefinită?"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "Doriți să folosiți compresie pentru BTRFS?"
+
+msgid "Would you like to create a separate partition for /home?"
+msgstr "Doriți să creați o partiție separată pentru /home?"
+
+msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n"
+msgstr "Dispozitivele de stocare selectare nu au o capacitate minimă necesară pentru o sugestie automată\n"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "Capacitatea minimă pentru partiția /home: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "Capacitatea minimă pentru partiția Arch Linux: {}GB"
+
+msgid "Continue"
+msgstr "Continuă"
+
+msgid "yes"
+msgstr "da"
+
+msgid "no"
+msgstr "nu"
+
+msgid "set: {}"
+msgstr "setează: {}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "Configurație manuală trebuie să fie o listă"
+
+msgid "No iface specified for manual configuration"
+msgstr "Nu a fost menționat iface pentru configurarea manuală"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr "Configurarea manuală nic fără un DHCP necesită o adresă IP"
+
+msgid "Add interface"
+msgstr "Adaugă interfață"
+
+msgid "Edit interface"
+msgstr "Editează interfața"
+
+msgid "Delete interface"
+msgstr "Șterge interfața"
+
+msgid "Select interface to add"
+msgstr "Selectează interfața pentru adăugare"
+
+msgid "Manual configuration"
+msgstr "Configurare manuală"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "Marchează/Demarchează o partiție ca fiind comprimată (doar btrfs)"
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "Parola utilizată pare să fie slabă, sigur doriți să o folosiți pe aceasta?"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr "Oferă o selecție de medii de lucru și manageri de ferestre precum GNOME, KDE, Sway"
+
+msgid "Select your desired desktop environment"
+msgstr "Selectați mediul de lucru dorit"
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr "O instalare de bază care vă permite să instalați Arch Linux așa cum vă doriți."
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr "Oferă o selecție de diferite pachete de sistem pentru instalare și activare precum httpd, nginx, mariadb"
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr "Alegeți serverele dorite pentru instalare, dacă nu este selectat niciunul se va realiza o instalare minimală"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr "Instalează un sistem minimal împreună cu xorg și driverele grafice."
+
+msgid "Press Enter to continue."
+msgstr "Apăsați Enter pentru a continua."
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr "Doriți să folosiți modul chroot pentru a intra în noua instalare și pentru a efectua configurații de post-instalare?"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "Sigur doriți să resetați această setare?"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "Selectați unul sau mai multe discuri dure pentru a utiliza și configura\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "Orice modificare aplicată la setările existene vor resta schema de partiționare a discului!"
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr "Dacă resetați selecția de discuri de stocare acest lucru va rezulta în resetarea schemei de partiționare existente a discului. Sigur doriți acest lucru?"
+
+msgid "Save and exit"
+msgstr "Salvează și ieși"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"{}\n"
+"conține partiții în așteptare, acest lucru le va șterge, sigur doriți acest lucru?"
+
+msgid "No audio server"
+msgstr "Nu există un server audio"
+
+msgid "(default)"
+msgstr "(predefinit)"
+
+msgid "Use ESC to skip"
+msgstr "Folosiți ESC pentru a ignora"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr ""
+"Folosi'i CTRL+C pentru a reseta selecția curentă\n"
+"\n"
+
+msgid "Copy to: "
+msgstr "Copiază la: "
+
+msgid "Edit: "
+msgstr "Editează: "
+
+msgid "Key: "
+msgstr "Cheie: "
+
+msgid "Edit {}: "
+msgstr "Editează {}: "
+
+msgid "Add: "
+msgstr "Adaugă: "
+
+msgid "Value: "
+msgstr "Valoare: "
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr "Puteți trece peste selectarea unui dispozitiv de stocare și folosiți orice configurație de partiționare este montată la /mnt (experimental)"
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "Selectați unul din discuri sau ignorați și folosiți /mnt ca predefinit"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "Selectați partițiile pentru a fi marcate pentru formatare:"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "Folosiți HSM pentru deblocarea dispozitivului criptat"
+
+msgid "Device"
+msgstr "Discpozitiv"
+
+msgid "Size"
+msgstr "Mărime"
+
+msgid "Free space"
+msgstr "Spațiu liber"
+
+msgid "Bus-type"
+msgstr "Tip magistrală"
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr "O parolă pentru utilizatorul root sau măcar un utilizator cu privilegii sudo trebuie adăugat"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "Introduceți numele de utilizator (lăsați gol pentru a ignora): "
+
+msgid "The username you entered is invalid. Try again"
+msgstr "Numele de utilizator adăugat este invalid. Încercați din nou"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "Ar trebui ca \"{}\" să fie un utilizator cu privilegii (sudo)?"
+
+msgid "Select which partitions to encrypt"
+msgstr "Selectați partițiile pentru criptare"
+
+msgid "very weak"
+msgstr "foarte slabă"
+
+msgid "weak"
+msgstr "slabă"
+
+msgid "moderate"
+msgstr "moderată"
+
+msgid "strong"
+msgstr "puetrnică"
+
+msgid "Add subvolume"
+msgstr "Adaugă subvolum"
+
+msgid "Edit subvolume"
+msgstr "Editează subvolum"
+
+msgid "Delete subvolume"
+msgstr "Șterge subvolum"
+
+msgid "Configured {} interfaces"
+msgstr "Configurează interfața {}"
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr "Această opținea setează numărul de descărcări paralele care pot avea loc în timpul instalării"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"Introduceți numărul de descărcări simultane pentru a fi activat.\n"
+" (Introduceți o valuare între 1 și {max_downloads})\n"
+"Info:"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Valoare minimă : 1 ( Permite o descărcare simultană, permite 2 descărcări simultane )"
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr " - Valoare minimă : 1 ( Permite o descărcare simultană, permite 2 descărcări simultane )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, permite o singură descărcare simultană )"
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr "Input invalid! Încercați din nou cu un input valid [1 pentru {max_downloads}, sau 0 pentru dezactivare]"
+
+msgid "Parallel Downloads"
+msgstr "Descărcări Simultane"
+
+msgid "ESC to skip"
+msgstr "ESC pentru ignorare"
+
+msgid "CTRL+C to reset"
+msgstr "CTRL+C pentru resetare"
+
+msgid "TAB to select"
+msgstr "TAB pentru a selecta"
+
+msgid "[Default value: 0] > "
+msgstr "[Valoare predefinită: 0] > "
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr "Pentru a putea folosi această traducere, vă rog să instalați manual un font compatibil cu această limbă."
+
+msgid "The font should be stored as {}"
+msgstr "Fontul ar trebui salvat ca {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall necesită privilegii avansate pentru execuție. Vedeți --help pentru mai multe."
+
+msgid "Select an execution mode"
+msgstr "Selectați un mod de execuție"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Nu se poate prelua profilul de la URL-ul specificat: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Profilurile trebuie să aibă nume unicate, dar definiții de profil cu nume duplicate au fost găsite: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Selectați unul sau mai multe discuri dure pentru a fi utilizate și configurate"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Dacă resetați selecția de discuri de stocare acest lucru va rezulta în resetarea schemei de partiționare existente a discului. Sigur doriți acest lucru?"
+
+msgid "Existing Partitions"
+msgstr "Partiții Existente"
+
+msgid "Select a partitioning option"
+msgstr "Selectați o opțiune de partiționare"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Introduceți directorul rădăcină a dispozitivelor montate: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Capacitatea minimă pentru partiția /home: {}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Capacitatea minimă pentru partiția Arch Linux: {}GB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Aceasta este o listă de profiluri pre-programate \"profiluris_bck\", ar putea să ușureze instalarea unor lucruri precum spațiile de lucru"
+
+msgid "Current profile selection"
+msgstr "Selecția curentă a profilului"
+
+msgid "Remove all newly added partitions"
+msgstr "Șterge toate partițiile noi adăugate"
+
+msgid "Assign mountpoint"
+msgstr "Asignează punctul de montare"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Marchează/Demarchează pentru formatare (șterge toate datele)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Marchează/Demarchează ca bootabil"
+
+msgid "Change filesystem"
+msgstr "Schimbă sistemul de fișiere"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Marchează/Demarchează ca comprimat"
+
+msgid "Set subvolumes"
+msgstr "Setează subvolumele"
+
+msgid "Delete partition"
+msgstr "Șterge partiția"
+
+msgid "Partition"
+msgstr "Partiție"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Partiția curentă este criptată, pentru a fi formatată un sistem de fișiere trebuie specificat"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Punctele de montare ale partițiilor sunt relevante pentru instalate, de exemplu partiția boot va fi /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Dacă punctul de montare /boot este setat, atunci partiția va fi marcată ca fiind bootabilă."
+
+msgid "Mountpoint: "
+msgstr "Punct de montare: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Sectoarele libere curente de pe dispozitiv {}:"
+
+msgid "Total sectors: {}"
+msgstr "Toate sectoarele: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Introduceți sectorul de început (predefinit: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Introduceți sectorul de final pentru partiționare (procentaj sau numărul block-ului, predefinit: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Această acțiune va șterge toate partițiile noi adăugate, continuați?"
+
+msgid "Partition management: {}"
+msgstr "Managementul partiției: {}"
+
+msgid "Total length: {}"
+msgstr "Lungime totală: {}"
+
+msgid "Encryption type"
+msgstr "Tipul de criptare"
+
+msgid "Partitions"
+msgstr "Partiții"
+
+msgid "No HSM devices available"
+msgstr "Nicun dispozitiv HSM disponibil"
+
+msgid "Partitions to be encrypted"
+msgstr "Selectați partițiile pentru criptare"
+
+msgid "Select disk encryption option"
+msgstr "Selectați opțiunea pentru criptarea discului"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "Selectați utilizarea unui device FID02 pentru HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Folosește cea mai bună configurație de partiționare"
+
+msgid "Manual Partitioning"
+msgstr "Partiționare manuală"
+
+msgid "Pre-mounted configuration"
+msgstr "Configurație pre-montată"
+
+msgid "Unknown"
+msgstr "Neștiut"
+
+msgid "Partition encryption"
+msgstr "Criptare partiție"
+
+msgid " ! Formatting {} in "
+msgstr " ! Se formatează {} in "
+
+msgid "↠Back"
+msgstr "↠Înapoi"
+
+msgid "Disk encryption"
+msgstr "Criptarea discului"
+
+msgid "Configuration"
+msgstr "Configurație"
+
+msgid "Password"
+msgstr "Parola"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "{} conține partiții în așteptare, acest proces le va șterge, doriți acest lucru?"
+
+msgid "Back"
+msgstr "ÃŽnapoi"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Vă rog selectați un manager de afișare pentru profilurile selectate: {}"
+
+msgid "Environment type: {}"
+msgstr "Tip mediu: {}"
+
+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 "Driverul proprietar Nvidia nu este compatibil cu Sway. Se poate să aveți probleme, sunteți ok cu asta?"
+
+msgid "Installed packages"
+msgstr "Pachete instalate"
+
+msgid "Add profile"
+msgstr "Adaugă profil"
+
+msgid "Edit profile"
+msgstr "Editează profil"
+
+msgid "Delete profile"
+msgstr "Șterge profil"
+
+msgid "Profile name: "
+msgstr "Nume profil: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Numele de utilizator adăugat este invalid. Încercați din nou"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Pachetele care se instalează cu acest profil (separare prin spațiu, lăsați liber pentru a ignora): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Serviciile activate cu acest profil (separare prin spațiu, lăsați liber pentru a ignora): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Ar trebui ca acest profil să fie bifat pentru instalare?"
+
+msgid "Create your own"
+msgstr "Crează-ți propriul tău"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"Selectați un driver grafic sau lăsați liber pentru a instala toate driverele open-source"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway are nevoie de acces la setup-ul dumneavoastră (colecție de dispozitive hardware ex. tastatura, mouse, etc.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Alegeți o opțiune pentru a acorda acces lui Sway la hardware"
+
+msgid "Graphics driver"
+msgstr "Driver grafic"
+
+msgid "Greeter"
+msgstr "Manager afișare"
+
+msgid "Please chose which greeter to install"
+msgstr "Vă rog selectați managerul de afișare pentru instalare"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Aceasta este o listă cu profiluri pre-programate"
+
+msgid "Disk configuration"
+msgstr "Configurație disc"
+
+msgid "Profiles"
+msgstr "Profiluri"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Caut directoare pentru salvarea fișierelor de configurare ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Selectați directorul (sau directoarele), pentru salvarea fișierelor de configurare"
+
+msgid "Add a custom mirror"
+msgstr "Adaugă o oglindă customizată"
+
+msgid "Change custom mirror"
+msgstr "Schimbă oglinda customizată"
+
+msgid "Delete custom mirror"
+msgstr "Șterge oglinda customizată"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Introduceți numele (lăsați gol pentru a ignora): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Introduceți URL (lăsați gol pentru a ignora): "
+
+msgid "Select signature check option"
+msgstr "Selectați opțiunea pentru verificarea semnăturii"
+
+msgid "Select signature option"
+msgstr "Selectați opțiunea pentru semnătură"
+
+msgid "Custom mirrors"
+msgstr "Oglinzi customizate"
+
+msgid "Defined"
+msgstr "Definit"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Salvează configurația utilizatorului (schema discului este inclusă)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Introduceți un director pentru salvarea configurării (configurațiilor, completare automată cu Tab activată)\n"
+"Directorul pentru salvare: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Doriți să salvați fișierele de configurare {} în această locație?\n"
+"\n"
+"{]"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Salvez {} fișierele de configurație la {}"
+
+msgid "Mirrors"
+msgstr "Oglinzi"
+
+msgid "Mirror regions"
+msgstr "Regiuni oglinzi"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - Valoare maximă : {} ( Permite {} descărcări simultane, permite {max_downloads+1} descărcări în același timp )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Input invalid! Încercați din nou cu un input valid [1 la {}, sau 0 pentru dezactivare]"
+
+msgid "Locales"
+msgstr "Locale"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Folosiți NetworkManager (necesar pentru configurarea setărilor de rețea într-un mod grafic pentru GNOME și KDE)"
+
+msgid "Total: {} / {}"
+msgstr "Total: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Toate valorile introduse pot fi notate cu unități precum: B, KB, KiB, MB, MiB ..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Dacă nicio unitate nu este specificate, atunci valorile vor fi interpretate ca sectoare"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Introduceți start (predefinit: sector {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Introduceți final (predefinit: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Nu se pot identifica dispozitive de tip fido2. Este libfido2 instalată?"
+
+msgid "Path"
+msgstr "Cale"
+
+msgid "Manufacturer"
+msgstr "Producător"
+
+msgid "Product"
+msgstr "Produs"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Configurație invalidă: {error}"
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Această opțiune setează numărul de descărcări paralele care pot avea loc în timpul instalării"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Introduceți numărul de descărcări simultane pentru a fi activat.\n"
+"\n"
+"Notă:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Valoare minimă : {} ( Permite {} descărcări simultane în același timp )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, permite o singură descărcare în același timp )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Input invalid! Încercați din nou cu un input valid [0 pentru dezactivare]"
+
+#, fuzzy
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway are nevoie de acces la setup-ul dumneavoastră (colecție de dispozitive hardware ex. tastatura, mouse, etc.)"
+
+#, fuzzy
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Alegeți o opțiune pentru a acorda acces lui Sway la hardware"
+
+#, fuzzy
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Toate valorile introduse pot fi notate cu unități precum: B, KB, KiB, MB, MiB ..."
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Doriți să folosiți swap pe zram?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Șterge profil"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Marchează/Demarchează ca bootabil"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Doriți să folosiți compresie pentru BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo
index 0fe08128..2a874109 100644
--- a/archinstall/locales/ru/LC_MESSAGES/base.mo
+++ b/archinstall/locales/ru/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po
index 1a33881f..48283335 100644
--- a/archinstall/locales/ru/LC_MESSAGES/base.po
+++ b/archinstall/locales/ru/LC_MESSAGES/base.po
@@ -10,7 +10,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
-"X-Generator: Poedit 3.1\n"
+"X-Generator: Poedit 3.4.2\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] ЗдеÑÑŒ был Ñоздан файл журнала: {} {}"
@@ -63,7 +63,7 @@ msgstr "Ðапишите дополнительные пакеты Ð´Ð»Ñ ÑƒÑÑ‚
msgid "Copy ISO network configuration to installation"
msgstr "Копировать Ñетевую конфигурацию ISO в уÑтановку"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "ИÑпользовать NetworkManager (необходим Ð´Ð»Ñ Ð³Ñ€Ð°Ñ„Ð¸Ñ‡ÐµÑкой наÑтройки интернета в GNOME и KDE)"
msgid "Select one network interface to configure"
@@ -98,10 +98,10 @@ msgid "Enter a desired filesystem type for the partition"
msgstr "Введите желаемый тип файловой ÑиÑтемы Ð´Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð°"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Введите начальное значение (в раздельных блоках: Ñ, ГБ, % и Ñ‚.д.; по умолчанию: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "Введите конечное значение (в раздельных блоках: Ñ, Гб, % и Ñ‚.д.; например: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} Ñодержит разделы в очереди, Ñто удалит их, вы уверены?"
@@ -794,18 +794,17 @@ msgstr "ÐаÑтроено интерфейÑов: {}"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Этот параметр определÑет количеÑтво параллельных загрузок, которые могут проиÑходить во Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Введите количеÑтво параллельных загрузок, которые будут включены.\n"
-" (Введите значение от 1 до {max_downloads})\n"
+" (Введите значение от 1 до {})\n"
"Примечание:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - МакÑимальное значение: {max_downloads} ( ПозволÑет {max_downloads} параллельных загрузок, позволÑет {max_downloads+1} загрузок одновременно )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Минимальное значение: {} ( ПозволÑет {} параллельную загрузку, позволÑет {} загрузки одновременно )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Минимальное значение: 1 ( ПозволÑет 1 параллельную загрузку, позволÑет 2 загрузки одновременно )"
@@ -838,55 +837,412 @@ msgstr "Чтобы иметь возможноÑÑ‚ÑŒ иÑпользовать Ñ
msgid "The font should be stored as {}"
msgstr "Шрифт должен быть Ñохранен как {}"
-#, fuzzy
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Ð”Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Archinstall требуютÑÑ Ð¿Ñ€Ð¸Ð²Ð¸Ð»ÐµÐ³Ð¸Ð¸ root. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñмотрите --help."
+
+msgid "Select an execution mode"
+msgstr "Выберите режим выполнениÑ"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Ðевозможно получить профиль из указанного url: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Профили должны иметь уникальное имÑ, но найдены Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ñ Ð´ÑƒÐ±Ð»Ð¸Ñ€ÑƒÑŽÑ‰Ð¸Ð¼ÑÑ Ð¸Ð¼ÐµÐ½ÐµÐ¼: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Выберите одно или неÑколько уÑтройÑтв Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ наÑтройки"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "ЕÑли вы ÑброÑите выбор уÑтройÑтва, Ñто также ÑброÑит текущую разметку диÑков. Ð’Ñ‹ уверены?"
+
+msgid "Existing Partitions"
+msgstr "СущеÑтвующие разделы"
+
+msgid "Select a partitioning option"
+msgstr "Выберите вариант разбивки на разделы"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Введите корневой каталог Ñмонтированных уÑтройÑтв: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Минимальный размер раздела /home: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Минимальный размер раздела Arch Linux: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Это ÑпиÑок предварительно запрограммированных профилей, они могут облегчить уÑтановку таких вещей, как Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ³Ð¾ Ñтола"
+
+msgid "Current profile selection"
+msgstr "Текущий выбор профилÑ"
+
+msgid "Remove all newly added partitions"
+msgstr "Удалить вÑе вновь добавленные разделы"
+
+msgid "Assign mountpoint"
+msgstr "Ðазначить точку монтированиÑ"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Пометить/ÑнÑÑ‚ÑŒ отметку Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (Ñтирание данных)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Пометить/ÑнÑÑ‚ÑŒ пометку как загрузочный"
+
+msgid "Change filesystem"
+msgstr "Изменить файловую ÑиÑтему"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Пометить/ÑнÑÑ‚ÑŒ отметку как Ñжатый"
+
+msgid "Set subvolumes"
+msgstr "УÑтановить подтома"
+
+msgid "Delete partition"
+msgstr "Удалить раздел"
+
+msgid "Partition"
+msgstr "Раздел"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Этот раздел в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½, Ð´Ð»Ñ ÐµÐ³Ð¾ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ указать файловую ÑиÑтему"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Точки Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð¾Ð² ÑвлÑÑŽÑ‚ÑÑ Ð¾Ñ‚Ð½Ð¾Ñительными внутри уÑтановки, например, загрузочный раздел будет /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "ЕÑли уÑтановлена точка Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ /boot, то раздел также будет помечен как загрузочный."
+
+msgid "Mountpoint: "
+msgstr "Точка монтированиÑ: "
+
+msgid "Current free sectors on device {}:"
+msgstr "Текущие Ñвободные Ñекторы на уÑтройÑтве {}:"
+
+msgid "Total sectors: {}"
+msgstr "Ð’Ñего Ñекторов: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Введите начальный Ñектор (по умолчанию: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Введите конечный Ñектор раздела (процент или номер блока, по умолчанию: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Это приведет к удалению вÑех вновь добавленных разделов, продолжить?"
+
+msgid "Partition management: {}"
+msgstr "Управление разделом: {}"
+
+msgid "Total length: {}"
+msgstr "ВеÑÑŒ размер: {}"
+
msgid "Encryption type"
-msgstr "Пароль шифрованиÑ"
+msgstr "Тип шифрованиÑ"
msgid "Partitions"
-msgstr ""
+msgstr "Разделы"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Ðет доÑтупных уÑтройÑтв HSM"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Выберите разделы Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ"
+msgstr "Разделы, подлежащие шифрованию"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "Выбрать вариант ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð¸Ñка"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "Выберите уÑтройÑтво FIDO2 Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве HSM"
+
+msgid "Use a best-effort default partition layout"
+msgstr "ИÑпользовать оптимальную Ñхему разделов по умолчанию"
+
+msgid "Manual Partitioning"
+msgstr "Ручное разбиение на разделы"
+
+msgid "Pre-mounted configuration"
+msgstr "Предварительно ÑÐ¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ"
+
+msgid "Unknown"
+msgstr "ÐеизвеÑтно"
+
+msgid "Partition encryption"
+msgstr "Шифрование раздела"
+
+msgid " ! Formatting {} in "
+msgstr " ! Форматирование {} в "
+
+msgid "↠Back"
+msgstr "↠Ðазад"
+
+msgid "Disk encryption"
+msgstr "Шифрование диÑка"
+
+msgid "Configuration"
+msgstr "КонфигурациÑ"
+
+msgid "Password"
+msgstr "Пароль"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} Ñодержит разделы в очереди, Ñто удалит их, вы уверены?"
+msgstr "Ð’Ñе наÑтройки будут Ñброшены, вы уверены?"
msgid "Back"
+msgstr "Ðазад"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "ПожалуйÑта, выберите, какой Ñкран приветÑÑ‚Ð²Ð¸Ñ ÑƒÑтановить Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… профилей: {}"
+
+msgid "Environment type: {}"
+msgstr "Тип Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ³Ð¾ Ñтола: {}"
+
+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 "Проприетарный драйвер Nvidia не поддерживаетÑÑ Sway. Вполне вероÑтно, что вы ÑтолкнетеÑÑŒ Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°Ð¼Ð¸, вы ÑоглаÑны Ñ Ñтим?"
+
+msgid "Installed packages"
+msgstr "УÑтанавливаемые пакеты"
+
+msgid "Add profile"
+msgstr "Добавить профиль"
+
+msgid "Edit profile"
+msgstr "Изменить профиль"
+
+msgid "Delete profile"
+msgstr "Удалить профиль"
+
+msgid "Profile name: "
+msgstr "Ð˜Ð¼Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Введенное вами Ð¸Ð¼Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ ÑƒÐ¶Ðµ иÑпользуетÑÑ. Попробуйте еще раз"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Пакеты, которые будут уÑтановлены Ñ Ñтим профилем (разделенные пробелами, оÑтавьте пуÑтым, чтобы пропуÑтить): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Службы, которые должны быть включены Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñтого Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ (разделенные пробелами, оÑтавьте пуÑтым, чтобы пропуÑтить): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Должен ли Ñтот профиль быть включен Ð´Ð»Ñ ÑƒÑтановки?"
+
+msgid "Create your own"
+msgstr "Создать Ñвой ÑобÑтвенный"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"Выберите графичеÑкий драйвер или оÑтавьте пуÑтым, чтобы уÑтановить вÑе драйверы Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway необходим доÑтуп к вашему компьютеру (набор аппаратных уÑтройÑтв, Ñ‚.е. клавиатура, мышь и Ñ‚.д.)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Выберите опцию, чтобы предоÑтавить Sway доÑтуп к вашему оборудованию"
-#, fuzzy
-msgid "Password"
-msgstr "Пароль root"
+msgid "Graphics driver"
+msgstr "ГрафичеÑкий драйвер"
-msgid "Partition encryption"
+msgid "Greeter"
+msgstr "Экран приветÑтвиÑ"
+
+msgid "Please chose which greeter to install"
+msgstr "ПожалуйÑта, выберите, какой Ñкран приветÑÑ‚Ð²Ð¸Ñ ÑƒÑтановить"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Это ÑпиÑок запрограммированных по умолчанию профилей"
+
+msgid "Disk configuration"
+msgstr "ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð´Ð¸Ñка"
+
+msgid "Profiles"
+msgstr "Профили"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "ПоиÑк возможных каталогов Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² конфигурации ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Выберите каталог (или каталоги) Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² конфигурации"
+
+msgid "Add a custom mirror"
+msgstr "Добавить пользовательÑкое зеркало"
+
+msgid "Change custom mirror"
+msgstr "Изменить пользовательÑкое зеркало"
+
+msgid "Delete custom mirror"
+msgstr "Удалить пользовательÑкое зеркало"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "Введите Ð¸Ð¼Ñ (оÑтавьте пуÑтым, чтобы пропуÑтить): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Введите Ð°Ð´Ñ€ÐµÑ (оÑтавьте пуÑтым, чтобы пропуÑтить): "
+
+msgid "Select signature check option"
+msgstr "Выбрать вариант проверки подпиÑи"
+
+msgid "Select signature option"
+msgstr "Выбрать вариант подпиÑи"
+
+msgid "Custom mirrors"
+msgstr "ПользовательÑкие зеркала"
+
+msgid "Defined"
+msgstr "Определено"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Сохранить конфигурацию Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ñ€Ð°Ð·Ð¼ÐµÑ‚ÐºÑƒ диÑка)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
msgstr ""
+"Введите каталог Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ (-ций) (включено заполнение вкладок)\n"
+"Каталог ÑохранениÑ: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"Хотите ли вы Ñохранить {} файл(-а) конфигурации в Ñледующем меÑте?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "Сохранение {} файла(-ов) конфигурации в {}"
+
+msgid "Mirrors"
+msgstr "Зеркала"
+
+msgid "Mirror regions"
+msgstr "Регионы зеркала"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - МакÑимальное значение: {} ( ПозволÑет {} параллельных загрузок, позволÑет {max_downloads+1} загрузок одновременно )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Ðеверный ввод! Повторите попытку Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¼ вводом [1 - {}, или 0 - отключить]"
+
+msgid "Locales"
+msgstr "Локализации"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "ИÑпользовать NetworkManager (необходим Ð´Ð»Ñ Ð³Ñ€Ð°Ñ„Ð¸Ñ‡ÐµÑкой наÑтройки интернета в GNOME и KDE)"
+
+msgid "Total: {} / {}"
+msgstr "ВеÑÑŒ размер: {} / {}"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Введите начальный Ñектор (процент или номер блока, по умолчанию: {}): "
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Ð’Ñе вводимые Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иметь ÑÑƒÑ„Ñ„Ð¸ÐºÑ Ñ ÐµÐ´Ð¸Ð½Ð¸Ñ†ÐµÐ¹ измерениÑ: Б, Кб, КиБ, Мб, МиБ..."
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Введите конечный Ñектор раздела (процент или номер блока, например: {}): "
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "ЕÑли единица Ð¸Ð·Ð¼ÐµÑ€ÐµÐ½Ð¸Ñ Ð½Ðµ указана, то значение интерпретируетÑÑ ÐºÐ°Ðº Ñектор"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Введите начало (по умолчанию: Ñектор {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Введите конец (по умолчанию: Ñектор {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Ðевозможно определить уÑтройÑтва fido2. УÑтановлена ли libfido2?"
+
+msgid "Path"
+msgstr "Путь"
+
+msgid "Manufacturer"
+msgstr "Производитель"
+
+msgid "Product"
+msgstr "Продукт"
#, python-brace-format
-#~ msgid "Edit {origkey} :"
-#~ msgstr "Редактировать {origkey}:"
+msgid "Invalid configuration: {error}"
+msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ: {error}"
+
+msgid "Type"
+msgstr "Тип"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Этот параметр определÑет количеÑтво параллельных загрузок, которые могут проиÑходить во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ пакетов"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Введите количеÑтво параллельных загрузок, которые будут включены.\n"
+"\n"
+"Примечание:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - МакÑимальное рекомендуемое значение: {} ( ПозволÑет {} параллельных загрузок одновременно )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволÑет только 1 загрузку за один раз )\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Ðеверный ввод! Повторите попытку Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¼ вводом [ или 0, чтобы отключить ]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland необходим доÑтуп к вашему компьютеру ( набор аппаратных уÑтройÑтв, Ñ‚.е. клавиатура, мышь и Ñ‚.д. )"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Выберите опцию, чтобы предоÑтавить Hyprland доÑтуп к вашему оборудованию"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Ð’Ñе вводимые Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иметь ÑÑƒÑ„Ñ„Ð¸ÐºÑ Ñ ÐµÐ´Ð¸Ð½Ð¸Ñ†ÐµÐ¹ измерениÑ: %, Б, Кб, КиБ, Мб, МиБ..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Хотите ли Ð’Ñ‹ иÑпользовать унифицированные образы Ñдра?"
+
+msgid "Unified kernel images"
+msgstr "Унифицированные образы Ñдра"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Ожидание Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñинхронизации времени (timedatectl show)."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ не завершена, пока вы ждете - проверьте документацию на предмет обходных путей: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "ПропуÑк Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкой Ñинхронизации времени (Ñто может привеÑти к проблемам, еÑли Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ ÑинхронизируетÑÑ Ð²Ð¾ Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки)"
-#~ msgid "Archinstall requires root privileges to run. See --help for more."
-#~ msgstr "Ð”Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Archinstall требуютÑÑ Ð¿Ñ€Ð¸Ð²Ð¸Ð»ÐµÐ³Ð¸Ð¸ root. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñмотрите --help."
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Ожидание Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñинхронизации ÑвÑзки ключей Arch Linux (archlinux-keyring-wkd-sync)."
-#~ msgid " ! Formatting {archinstall.arguments['harddrives']} in "
-#~ msgstr " ! Форматирование {archinstall.arguments['harddrives']} в "
+msgid "Selected profiles: "
+msgstr "Выбранные профили: "
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ не завершена, пока вы ждете - проверьте документацию на предмет обходных путей: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Пометить/ÑнÑÑ‚ÑŒ пометку как загрузочный"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Хотите ли вы иÑпользовать Ñжатие BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/sv/LC_MESSAGES/base.mo b/archinstall/locales/sv/LC_MESSAGES/base.mo
index 243f1d9f..11d757b0 100644
--- a/archinstall/locales/sv/LC_MESSAGES/base.mo
+++ b/archinstall/locales/sv/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po
index 61755a2c..efc75172 100644
--- a/archinstall/locales/sv/LC_MESSAGES/base.po
+++ b/archinstall/locales/sv/LC_MESSAGES/base.po
@@ -63,7 +63,7 @@ msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag
msgid "Copy ISO network configuration to installation"
msgstr "Kopiera nätverkskonfigurationen från ISO till installationen"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "Använd NetworkManager (nödvändig för konfigurera internet i grafiska miljöerna GNOME och KDE)"
msgid "Select one network interface to configure"
@@ -804,14 +804,13 @@ msgstr ""
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr ""
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
msgstr ""
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
@@ -846,6 +845,125 @@ msgstr ""
msgid "The font should be stored as {}"
msgstr ""
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "Välj vad du vill göra med '{}'"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "Välj en eller flera hårddiskar som skall användas och konfigureras"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Om du återställer hårddiskvalen kommer det också återställa diskuppsättningen. Är du säker?"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "Skapar en partition...."
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "Ta bort en partition"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Välj vilken mapp du vill spara konfigurerationerna till: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Minsta kapaciteten för /home är: {}GB\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Minsta kapaciteten för Arch Linux partitionen är: {}GB"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Detta är en lista med förprogrammerade profiler, dom kan göra installation av exempelvis skrivbordsmiljöer lite enklare"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "Nuvarande partioneringslayout"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "Skapa en ny partition"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "Välj monteringspunkt för en partition"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Markera/Avmarkera en partition för formatering (tar bort alla data)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "Markera/Avmarkera en partition för komprimering (BTRFS enbart)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "Ta bort användare"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "Ta bort en partition"
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Partitionens monteringsplats är relativa till insidan av installationen, boot är exempelvis /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "Inte en giltig mapp: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Mata in slutsektor för partitionen (procent eller block-nummer, ex: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
#, fuzzy
msgid "Encryption type"
msgstr "Välj ett krypterings-lösenord"
@@ -868,24 +986,313 @@ msgid "Select a FIDO2 device to use for HSM"
msgstr ""
#, fuzzy
-msgid "All settings will be reset, are you sure?"
-msgstr "{} innehåller uppköade partitionen och detta kommer ta bort dessa. Är du säker?"
+msgid "Use a best-effort default partition layout"
+msgstr "Töm alla partitioner och använd en generiskt rekommenderad partitionslayout"
-msgid "Back"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "Manuell konfiguration"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "Ingen konfiguration"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
#, fuzzy
+msgid "Configuration"
+msgstr "Ingen konfiguration"
+
+#, fuzzy
msgid "Password"
msgstr "root-lösenord"
-msgid "Partition encryption"
+#, fuzzy
+msgid "All settings will be reset, are you sure?"
+msgstr "{} innehåller uppköade partitionen och detta kommer ta bort dessa. Är du säker?"
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+#, fuzzy
+msgid "Installed packages"
+msgstr "Extra paket"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "Profil"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "Profil"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "Ta bort interface"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "Profil"
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag, lämna tom för att skippa): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag, lämna tom för att skippa): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Välj en grafikdrivrutin eller lämna blank för att installera alla med publika drivrutiner"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "Ingen konfiguration"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "Profil"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Välj en eller flera hårddiskar som skall användas och konfigureras"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "Lägg till användare"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "Mata in ett användarnamn för att skapa ytterligare användare (lämna tom för att hoppa över): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "Mata in ett användarnamn för att skapa ytterligare användare (lämna tom för att hoppa över): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "Välj hårddisk-layout"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "Välj hårddisk-layout"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "Spara användarkonfigurering"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "Välj vilken mapp du vill spara konfigurerationerna till: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "Spara konfigurationen"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "Region för paketsynk"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "Region för paketsynk"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): "
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "Använd NetworkManager (nödvändig för konfigurera internet i grafiska miljöerna GNOME och KDE)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Manuell konfiguration"
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Mata in slutsektor för partitionen (procent eller block-nummer, ex: {}): "
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Vill du använda swap under zram?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Ta bort interface"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Markera/Avmarkera en partition för komprimering (BTRFS enbart)"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Vill du använda komprimering för BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo
index 3f175509..595ab950 100644
--- a/archinstall/locales/ta/LC_MESSAGES/base.mo
+++ b/archinstall/locales/ta/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po
index 4f1d6762..3f0bdf01 100644
--- a/archinstall/locales/ta/LC_MESSAGES/base.po
+++ b/archinstall/locales/ta/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.1.1\n"
+"X-Generator: Poedit 3.4.2\n"
msgid "[!] A log file has been created here: {} {}"
msgstr "[!] ஒர௠பதிவ௠கோபà¯à®ªà¯ இஙà¯à®•à¯‡ உரà¯à®µà®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯: {} {}"
@@ -62,8 +62,8 @@ msgstr "நிறà¯à®µ கூடà¯à®¤à®²à¯ தொகà¯à®ªà¯à®ªà¯à®•à®³à¯ˆ
msgid "Copy ISO network configuration to installation"
msgstr "நிறà¯à®µà®²à¯à®•à¯à®•à¯ ISO பிணைய கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯ நகலெடà¯à®•à¯à®•à®µà¯à®®à¯"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
-msgstr "பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯ NetworkManager à®à®ªà¯(GNOME மறà¯à®±à¯à®®à¯ KDE இல௠இணையதà¯à®¤à¯ˆ வரைகலை à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ கடà¯à®Ÿà®®à¯ˆà®•à¯à®• அவசியமà¯)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯ NetworkManager à®à®ªà¯ (GNOME மறà¯à®±à¯à®®à¯ KDE இல௠இணையதà¯à®¤à¯ˆ வரைகலை à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ கடà¯à®Ÿà®®à¯ˆà®•à¯à®• அவசியமà¯)"
msgid "Select one network interface to configure"
msgstr "கடà¯à®Ÿà®®à¯ˆà®•à¯à®• ஒர௠பிணைய இடைமà¯à®•à®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
@@ -72,7 +72,7 @@ msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{
msgstr "எநà¯à®¤ பயனà¯à®®à¯à®±à¯ˆà®¯à¯ˆ \"{}\" உளà¯à®³à®®à¯ˆà®•à¯à®• வேணà¯à®Ÿà¯à®®à¯ எனà¯à®ªà®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯ அலà¯à®²à®¤à¯ இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ பயனà¯à®®à¯à®±à¯ˆ \"{}\" à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ தவிரà¯à®•à¯à®•à®µà¯à®®à¯"
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
-msgstr "உளà¯à®³à®¿à®Ÿà¯à®™à¯à®•à®³à¯ IP மறà¯à®±à¯à®®à¯ சபà¯à®¨à¯†à®Ÿà¯à®Ÿà¯ˆ {}(எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà¯: 192.168.0.5/24): "
+msgstr "உளà¯à®³à®¿à®Ÿà¯à®™à¯à®•à®³à¯ IP மறà¯à®±à¯à®®à¯ சபà¯à®¨à¯†à®Ÿà¯à®Ÿà¯ˆ {} (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà¯: 192.168.0.5/24): "
msgid "Enter your gateway (router) IP address or leave blank for none: "
msgstr "உஙà¯à®•à®³à¯ நà¯à®´à¯ˆà®µà®¾à®¯à®¿à®²à¯ (திசைவி) IP à®®à¯à®•à®µà®°à®¿à®¯à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ அலà¯à®²à®¤à¯ எதறà¯à®•à¯à®®à¯ காலியாக விடவà¯à®®à¯: "
@@ -97,10 +97,10 @@ msgid "Enter a desired filesystem type for the partition"
msgstr "பகிரà¯à®µà¯à®•à¯à®•à¯ தேவையான கோபà¯à®ªà¯ à®®à¯à®±à¯ˆà®®à¯ˆ வகையை உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "தொடகà¯à®• இடதà¯à®¤à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (பிரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ அலகà¯à®•à®³à®¿à®²à¯: s, GB, %, à®®à¯à®¤à®²à®¿à®¯à®© ; இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "இறà¯à®¤à®¿ இடதà¯à®¤à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (பிரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ அலகà¯à®•à®³à®¿à®²à¯: s, GB, %, etc. ; ex: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} வரிசைபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿ பகிரà¯à®µà¯à®•à®³à¯ˆà®•à¯ கொணà¯à®Ÿà¯à®³à¯à®³à®¤à¯, இத௠அவறà¯à®±à¯ˆ அகறà¯à®±à¯à®®à¯, நீஙà¯à®•à®³à¯ உறà¯à®¤à®¿à®¯à®¾à®• இரà¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?"
@@ -295,7 +295,7 @@ msgid "Automatic time sync (NTP)"
msgstr "தானியஙà¯à®•à®¿ நேர ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ (NTP)"
msgid "Install ({} config(s) missing)"
-msgstr "நிறà¯à®µà¯ ({} config(களà¯) இலà¯à®²à¯ˆ)"
+msgstr "நிறà¯à®µà¯ ({} கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯(களà¯) இலà¯à®²à¯ˆ)"
msgid ""
"You decided to skip harddrive selection\n"
@@ -367,7 +367,7 @@ msgid "Enter a password: "
msgstr "கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯: "
msgid "Enter a encryption password for {}"
-msgstr "{}கà¯à®•à®¾à®© எனà¯à®•à¯à®°à®¿à®ªà¯à®·à®©à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯"
+msgstr "{} கà¯à®•à®¾à®© எனà¯à®•à¯à®°à®¿à®ªà¯à®·à®©à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯"
msgid "Enter disk encryption password (leave blank for no encryption): "
msgstr "வடà¯à®Ÿà¯ கà¯à®±à®¿à®¯à®¾à®•à¯à®• கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯ இலà¯à®²à®¾à®®à®²à¯ இரà¯à®ªà¯à®ªà®¤à®±à¯à®•à¯ காலியாக விடவà¯à®®à¯): "
@@ -426,7 +426,7 @@ msgid "Delete"
msgstr "அழி"
msgid "Select an action for '{}'"
-msgstr "'{}'கà¯à®•à®¾à®© செயலைத௠தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+msgstr "'{}' கà¯à®•à®¾à®© செயலைத௠தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
msgid "Copy to new key:"
msgstr "பà¯à®¤à®¿à®¯ விசைகà¯à®•à¯ நகலெடà¯:"
@@ -793,18 +793,17 @@ msgstr "கடà¯à®Ÿà®®à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ {} இடைமà¯à®•à®™à¯
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "இநà¯à®¤ விரà¯à®ªà¯à®ªà®®à¯ நிறà¯à®µà®²à®¿à®©à¯ போத௠நிகழகà¯à®•à¯‚டிய இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à®¿à®©à¯ எணà¯à®£à®¿à®•à¯à®•à¯ˆà®¯à¯ˆ செயலà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à®¤à¯"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"இயகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯ இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à®¿à®©à¯ எணà¯à®£à®¿à®•à¯à®•à¯ˆà®¯à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯.\n"
-" (1 à®®à¯à®¤à®²à¯ {max_downloads} வரையிலான மதிபà¯à®ªà¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯)\n"
+" (1 à®®à¯à®¤à®²à¯ {} வரையிலான மதிபà¯à®ªà¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯)\n"
"கà¯à®±à®¿à®ªà¯à®ªà¯:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - அதிகபடà¯à®š மதிபà¯à®ªà¯ : {max_downloads} ( {max_downloads} இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯, ஒரே நேரதà¯à®¤à®¿à®²à¯ {max_downloads+1} பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯ )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - அதிகபடà¯à®š மதிபà¯à®ªà¯ : {} ( {} இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯, ஒரே நேரதà¯à®¤à®¿à®²à¯ {} பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯ )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - கà¯à®±à¯ˆà®¨à¯à®¤à®ªà®Ÿà¯à®š மதிபà¯à®ªà¯ : 1 (1 இணை பதிவிறகà¯à®•à®¤à¯à®¤à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯, ஒர௠நேரதà¯à®¤à®¿à®²à¯ 2 பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯ )"
@@ -829,53 +828,422 @@ msgid "TAB to select"
msgstr "தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®• TAB"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ மதிபà¯à®ªà¯: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "இநà¯à®¤ மொழிபெயரà¯à®ªà¯à®ªà¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤, மொழியை ஆதரிகà¯à®•à¯à®®à¯ எழà¯à®¤à¯à®¤à¯à®°à¯à®µà¯ˆ கைமà¯à®±à¯ˆà®¯à®¾à®• நிறà¯à®µà®µà¯à®®à¯."
msgid "The font should be stored as {}"
+msgstr "எழà¯à®¤à¯à®¤à¯à®°à¯ {} ஆக சேமிகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà¯à®®à¯"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall ஠இயகà¯à®• ரூட௠சிறபà¯à®ªà¯à®°à®¿à®®à¯ˆà®•à®³à¯ தேவை. மேலà¯à®®à¯ பாரà¯à®•à¯à®• --help."
+
+msgid "Select an execution mode"
+msgstr "செயலà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ பயனà¯à®®à¯à®±à¯ˆà®¯à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿ url இலிரà¯à®¨à¯à®¤à¯ சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯ˆà®ªà¯ பெற à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "சà¯à®¯à®µà®¿à®µà®°à®™à¯à®•à®³à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ பெயரைக௠கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®• வேணà¯à®Ÿà¯à®®à¯, ஆனால௠நகல௠பெயரà¯à®Ÿà®©à¯ சà¯à®¯à®µà®¿à®µà®° வரையறைகள௠காணபà¯à®ªà®Ÿà¯à®•à®¿à®©à¯à®±à®©: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ மறà¯à®±à¯à®®à¯ கடà¯à®Ÿà®®à¯ˆà®•à¯à®• ஒனà¯à®±à¯ அலà¯à®²à®¤à¯ அதறà¯à®•à¯ மேறà¯à®ªà®Ÿà¯à®Ÿ சாதனஙà¯à®•à®³à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "சாதனத௠தேரà¯à®µà¯ˆ மீடà¯à®Ÿà®®à¯ˆà®¤à¯à®¤à®¾à®²à¯, இத௠தறà¯à®ªà¯‹à®¤à¯ˆà®¯ வடà¯à®Ÿà¯ அமைபà¯à®ªà¯ˆà®¯à¯à®®à¯ மீடà¯à®Ÿà®®à¯ˆà®•à¯à®•à¯à®®à¯. நீஙà¯à®•à®³à¯ உறà¯à®¤à®¿à®¯à®¾à®• இரà¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?"
+
+msgid "Existing Partitions"
+msgstr "à®à®±à¯à®•à®©à®µà¯‡ உளà¯à®³ பகிரà¯à®µà¯à®•à®³à¯"
+
+msgid "Select a partitioning option"
+msgstr "பகிரà¯à®µà¯ விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "à®à®±à¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿ சாதனஙà¯à®•à®³à®¿à®©à¯ ரூட௠கோபà¯à®ªà®•à®¤à¯à®¤à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
msgstr ""
+"/home பகிரà¯à®µà¯à®•à¯à®•à®¾à®© கà¯à®±à¯ˆà®¨à¯à®¤à®ªà®Ÿà¯à®š கொளà¯à®³à®³à®µà¯: {}GiB\n"
+"\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "ஆரà¯à®šà¯ லினகà¯à®¸à¯ பகிரà¯à®µà¯à®•à¯à®•à®¾à®© கà¯à®±à¯ˆà®¨à¯à®¤à®ªà®Ÿà¯à®š கொளà¯à®³à®³à®µà¯: {}GiB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "இத௠மà¯à®©à¯-திடà¯à®Ÿà®®à®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿ profiles_bck இன௠படà¯à®Ÿà®¿à®¯à®²à¯, அவை டெஸà¯à®•à¯à®Ÿà®¾à®ªà¯ சூழலà¯à®•à®³à¯ போனà¯à®±à®µà®±à¯à®±à¯ˆ நிறà¯à®µà¯à®µà®¤à¯ˆ எளிதாகà¯à®•à®²à®¾à®®à¯"
+
+msgid "Current profile selection"
+msgstr "தறà¯à®ªà¯‹à®¤à¯ˆà®¯ சà¯à®¯à®µà®¿à®µà®°à®¤à¯ தேரà¯à®µà¯"
+
+msgid "Remove all newly added partitions"
+msgstr "பà¯à®¤à®¿à®¤à®¾à®• சேரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ அனைதà¯à®¤à¯ பகிரà¯à®µà¯à®•à®³à¯ˆà®¯à¯à®®à¯ அகறà¯à®±à®µà¯à®®à¯"
+
+msgid "Assign mountpoint"
+msgstr "பகிரà¯à®µà¯à®•à¯à®•à¯ à®à®±à¯à®±-பà¯à®³à¯à®³à®¿à®¯à¯ˆ ஒதà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "வடிவமைகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯à®µà¯ˆà®¯à¯ˆ கà¯à®±à®¿/கà¯à®±à®¿à®¨à¯€à®•à¯à®•à¯ (தரவை அழிகà¯à®•à®¿à®±à®¤à¯)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "தà¯à®µà®•à¯à®•à®•à¯à®•à¯‚டியதாகக௠கà¯à®±à®¿/கà¯à®±à®¿à®¨à¯€à®•à¯à®•à¯"
+
+msgid "Change filesystem"
+msgstr "கோபà¯à®ªà¯ à®®à¯à®±à¯ˆà®®à¯ˆà®¯à¯ˆ மாறà¯à®±à®µà¯à®®à¯"
+
+msgid "Mark/Unmark as compressed"
+msgstr "சà¯à®°à¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à®¾à®•à®•à¯ கà¯à®±à®¿/கà¯à®±à®¿à®¨à¯€à®•à¯à®•à¯"
+
+msgid "Set subvolumes"
+msgstr "தà¯à®£à¯ˆà®¤à¯ தொகà¯à®¤à®¿à®•à®³à¯ˆ அமைகà¯à®•à®µà¯à®®à¯"
+
+msgid "Delete partition"
+msgstr "பகிரà¯à®µà¯ˆ நீகà¯à®•à¯"
+
+msgid "Partition"
+msgstr "பகிரà¯à®µà¯"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "இநà¯à®¤ பகிரà¯à®µà¯ தறà¯à®ªà¯‹à®¤à¯ கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯ செயà¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯, அதை வடிவமைகà¯à®• ஒர௠கோபà¯à®ªà¯ à®®à¯à®±à¯ˆà®®à¯ˆ கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà¯à®®à¯"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "பகிரà¯à®µà¯ மவà¯à®£à¯à®Ÿà¯-பாயிணà¯à®Ÿà¯à®•à®³à¯ நிறà¯à®µà®²à®¿à®©à¯ உளà¯à®³à¯‡ தொடரà¯à®ªà¯à®Ÿà¯ˆà®¯à®µà¯ˆ, தà¯à®µà®•à¯à®•à®®à¯ /boot எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®• இரà¯à®•à¯à®•à¯à®®à¯."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "மவà¯à®£à¯à®Ÿà¯à®ªà®¾à®¯à®¿à®£à¯à®Ÿà¯ /boot அமைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¾à®²à¯, பகிரà¯à®µà¯ தà¯à®µà®•à¯à®•à®•à¯à®•à¯‚டியதாகக௠கà¯à®±à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯."
+
+msgid "Mountpoint: "
+msgstr "மவà¯à®£à¯à®Ÿà¯à®ªà®¾à®¯à®¿à®£à¯à®Ÿà¯: "
+
+msgid "Current free sectors on device {}:"
+msgstr "{} சாதனதà¯à®¤à®¿à®²à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®¯ இலவசப௠பிரிவà¯à®•à®³à¯:"
+
+msgid "Total sectors: {}"
+msgstr "மொதà¯à®¤ பிரிவà¯à®•à®³à¯: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "தொடகà¯à®•à®ªà¯ பிரிவை உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "பகிரà¯à®µà®¿à®©à¯ இறà¯à®¤à®¿à®ªà¯ பகà¯à®¤à®¿à®¯à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (சதவீதம௠அலà¯à®²à®¤à¯ தொகà¯à®¤à®¿ எணà¯, இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "இத௠பà¯à®¤à®¿à®¤à®¾à®• சேரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ அனைதà¯à®¤à¯ பகிரà¯à®µà¯à®•à®³à¯ˆà®¯à¯à®®à¯ அகறà¯à®±à¯à®®à¯, தொடரவா?"
+
+msgid "Partition management: {}"
+msgstr "பகிரà¯à®µà¯ மேலாணà¯à®®à¯ˆ: {}"
+
+msgid "Total length: {}"
+msgstr "à®®à¯à®´à¯ நீளமà¯: {}"
-#, fuzzy
msgid "Encryption type"
-msgstr "கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯"
+msgstr "கà¯à®±à®¿à®¯à®¾à®•à¯à®• வகை"
msgid "Partitions"
-msgstr ""
+msgstr "பகிரà¯à®µà¯à®•à®³à¯"
msgid "No HSM devices available"
-msgstr ""
+msgstr "HSM சாதனஙà¯à®•à®³à¯ எதà¯à®µà¯à®®à¯ இலà¯à®²à¯ˆ"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯ செயà¯à®¯ வேணà¯à®Ÿà®¿à®¯ பகிரà¯à®µà¯à®•à®³à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+msgstr "கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯ செயà¯à®¯à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯ பகிரà¯à®µà¯à®•à®³à¯"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "வடà¯à®Ÿà¯ கà¯à®±à®¿à®¯à®¾à®•à¯à®• விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "HSM கà¯à®•à¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ FIDO2 சாதனதà¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Use a best-effort default partition layout"
+msgstr "சிறநà¯à®¤ à®®à¯à®¯à®±à¯à®šà®¿ இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ பகிரà¯à®µà¯ தளவமைபà¯à®ªà¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®µà¯à®®à¯"
+
+msgid "Manual Partitioning"
+msgstr "கைமà¯à®±à¯ˆ பகிரà¯à®µà¯"
+
+msgid "Pre-mounted configuration"
+msgstr "à®®à¯à®©à¯-à®à®±à¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿ கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯"
+
+msgid "Unknown"
+msgstr "தெரியவிலà¯à®²à¯ˆ/தெரியாததà¯"
+
+msgid "Partition encryption"
+msgstr "பகிரà¯à®µà¯ கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯"
+
+msgid " ! Formatting {} in "
+msgstr " ! வடிவமைதà¯à®¤à®²à¯ {} உளà¯à®³ "
+
+msgid "↠Back"
+msgstr "↠பினà¯"
+
+msgid "Disk encryption"
+msgstr "வடà¯à®Ÿà¯ கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®®à¯"
+
+msgid "Configuration"
+msgstr "கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯"
+
+msgid "Password"
+msgstr "கடவà¯à®šà¯à®šà¯†à®¾à®²à¯"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} வரிசைபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿ பகிரà¯à®µà¯à®•à®³à¯ˆà®•à¯ கொணà¯à®Ÿà¯à®³à¯à®³à®¤à¯, இத௠அவறà¯à®±à¯ˆ அகறà¯à®±à¯à®®à¯, நீஙà¯à®•à®³à¯ உறà¯à®¤à®¿à®¯à®¾à®• இரà¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?"
+msgstr "எலà¯à®²à®¾ அமைபà¯à®ªà¯à®•à®³à¯à®®à¯ மீடà¯à®Ÿà®®à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯, உறà¯à®¤à®¿à®¯à®¾à®• இரà¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?"
msgid "Back"
+msgstr "திரà¯à®®à¯à®ªà®µà¯à®®à¯"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ சà¯à®¯à®µà®¿à®µà®°à®™à¯à®•à®³à¯à®•à¯à®•à¯ எநà¯à®¤ வாழà¯à®¤à¯à®¤à¯à®°à¯ˆ நிறà¯à®µ வேணà¯à®Ÿà¯à®®à¯ எனà¯à®ªà®¤à¯ˆà®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¯à®µà¯à®®à¯: {}"
+
+msgid "Environment type: {}"
+msgstr "சà¯à®±à¯à®±à¯à®šà¯à®šà¯‚ழல௠வகை: {}"
+
+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 "தனியà¯à®°à®¿à®® Nvidia இயகà¯à®•à®¿ ஸà¯à®µà¯‡ ஆல௠ஆதரிகà¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ. நீஙà¯à®•à®³à¯ சிகà¯à®•à®²à®¿à®²à¯ சிகà¯à®• வாயà¯à®ªà¯à®ªà¯à®³à¯à®³à®¤à¯, உனகà¯à®•à¯ அத௠சரியா?"
+
+msgid "Installed packages"
+msgstr "நிறà¯à®µà®ªà¯à®ªà®Ÿà¯à®Ÿ தொகà¯à®ªà¯à®ªà¯à®•à®³à¯"
+
+msgid "Add profile"
+msgstr "சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯ˆà®šà¯ சேரà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Edit profile"
+msgstr "சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯ˆà®¤à¯ திரà¯à®¤à¯à®¤à®µà¯à®®à¯"
+
+msgid "Delete profile"
+msgstr "சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯ˆ நீகà¯à®•à¯"
+
+msgid "Profile name: "
+msgstr "சà¯à®¯à®µà®¿à®µà®°à®ªà¯ பெயரà¯: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "நீஙà¯à®•à®³à¯ உளà¯à®³à®¿à®Ÿà¯à®Ÿ சà¯à®¯à®µà®¿à®µà®°à®ªà¯ பெயர௠à®à®±à¯à®•à®©à®µà¯‡ பயனà¯à®ªà®¾à®Ÿà¯à®Ÿà®¿à®²à¯ உளà¯à®³à®¤à¯. மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "இநà¯à®¤ சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯à®Ÿà®©à¯ நிறà¯à®µà®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯ தொகà¯à®ªà¯à®ªà¯à®•à®³à¯ (இடம௠பிரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯, தவிரà¯à®•à¯à®• காலியாக விடவà¯à®®à¯): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "இநà¯à®¤à®šà¯ சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯à®Ÿà®©à¯ இயகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯ சேவைகள௠(இடம௠பிரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯, தவிரà¯à®•à¯à®• காலியாக விடவà¯à®®à¯): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "நிறà¯à®µà®²à¯à®•à¯à®•à¯ இநà¯à®¤ சà¯à®¯à®µà®¿à®µà®°à®®à¯ இயகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà¯à®®à®¾?"
+
+msgid "Create your own"
+msgstr "சொநà¯à®¤à®®à®¾à®• உரà¯à®µà®¾à®•à¯à®•à®µà¯à®®à¯"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"அனைதà¯à®¤à¯ திறநà¯à®¤ மூல இயகà¯à®•à®¿à®•à®³à¯ˆà®¯à¯à®®à¯ நிறà¯à®µ வரைகலை இயகà¯à®•à®¿à®•à®³à¯ˆ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯ அலà¯à®²à®¤à¯ காலியாக விடவà¯à®®à¯"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "ஸà¯à®µà¯‡à®•à¯à®•à¯ உஙà¯à®•à®³à¯ இரà¯à®•à¯à®•à¯ˆà®•à¯à®•à®¾à®© அணà¯à®•à®²à¯ தேவை (வனà¯à®ªà¯Šà®°à¯à®³à¯ சாதனஙà¯à®•à®³à®¿à®©à¯ சேகரிபà¯à®ªà¯ அதாவத௠விசைபà¯à®ªà®²à®•à¯ˆ, சà¯à®Ÿà¯à®Ÿà®¿ போனà¯à®±à®µà¯ˆ)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"உஙà¯à®•à®³à¯ வனà¯à®ªà¯Šà®°à¯à®³à¯à®•à¯à®•à®¾à®© அணà¯à®•à®²à¯ˆ வழஙà¯à®•à¯à®µà®¤à®±à¯à®•à®¾à®© விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¯à®µà¯à®®à¯"
-#, fuzzy
-msgid "Password"
-msgstr "ரூட௠கடவà¯à®šà¯à®šà¯Šà®²à¯"
+msgid "Graphics driver"
+msgstr "வரைகலை இயகà¯à®•à®¿"
-msgid "Partition encryption"
+msgid "Greeter"
+msgstr "வாழà¯à®¤à¯à®¤à¯à®ªà®µà®°à¯"
+
+msgid "Please chose which greeter to install"
+msgstr "எநà¯à®¤ வாழà¯à®¤à¯à®¤à¯à®°à¯ˆ நிறà¯à®µ வேணà¯à®Ÿà¯à®®à¯ எனà¯à®ªà®¤à¯ˆà®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¯à®µà¯à®®à¯"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "இத௠மà¯à®©à¯-திடà¯à®Ÿà®®à®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿ default_profiles படà¯à®Ÿà®¿à®¯à®²à¯"
+
+msgid "Disk configuration"
+msgstr "வடà¯à®Ÿà¯ கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯"
+
+msgid "Profiles"
+msgstr "சà¯à®¯à®µà®¿à®µà®°à®™à¯à®•à®³à¯"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "உளà¯à®³à®®à¯ˆà®µà¯ கோபà¯à®ªà¯à®•à®³à¯ˆà®šà¯ சேமிகà¯à®• சாதà¯à®¤à®¿à®¯à®®à®¾à®© கோபà¯à®ªà®•à®™à¯à®•à®³à¯ˆà®•à¯ கணà¯à®Ÿà®±à®¿à®¤à®²à¯ ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "உளà¯à®³à®®à¯ˆà®µà¯ கோபà¯à®ªà¯à®•à®³à¯ˆ சேமிகà¯à®• கோபà¯à®ªà®•à®¤à¯à®¤à¯ˆ (அலà¯à®²à®¤à¯ கோபà¯à®ªà®•à®™à¯à®•à®³à¯ˆ) தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Add a custom mirror"
+msgstr "தனிபà¯à®ªà®¯à®©à¯ கணà¯à®£à®¾à®Ÿà®¿à®¯à¯ˆà®šà¯ சேரà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Change custom mirror"
+msgstr "தனிபà¯à®ªà®¯à®©à¯ கணà¯à®£à®¾à®Ÿà®¿à®¯à¯ˆ மாறà¯à®±à®µà¯à®®à¯"
+
+msgid "Delete custom mirror"
+msgstr "தனிபà¯à®ªà®¯à®©à¯ கணà¯à®£à®¾à®Ÿà®¿à®¯à¯ˆ நீகà¯à®•à¯"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "பெயரை உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (தவிரà¯à®•à¯à®• காலியாக விடவà¯à®®à¯): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "URL ஠உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (தவிரà¯à®•à¯à®• காலியாக விடவà¯à®®à¯): "
+
+msgid "Select signature check option"
+msgstr "கையொபà¯à®ª சரிபாரà¯à®ªà¯à®ªà¯ விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Select signature option"
+msgstr "கையெழà¯à®¤à¯à®¤à¯ விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Custom mirrors"
+msgstr "தனிபà¯à®ªà®¯à®©à¯ கணà¯à®£à®¾à®Ÿà®¿à®•à®³à¯"
+
+msgid "Defined"
+msgstr "வரையறà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "பயனர௠உளà¯à®³à®®à¯ˆà®µà¯ˆà®šà¯ சேமிகà¯à®•à®µà¯à®®à¯ (வடà¯à®Ÿà¯ தளவமைபà¯à®ªà¯ உடà¯à®ªà®Ÿ)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"உளà¯à®³à®®à¯ˆà®µà¯(களை) சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®µà®¤à®±à¯à®•à®¾à®© கோபà¯à®ªà®•à®¤à¯à®¤à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (தாவல௠நிறைவ௠இயகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯)\n"
+"கோபà¯à®ªà®•à®¤à¯à®¤à¯ˆ சேமி: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"பினà¯à®µà®°à¯à®®à¯ இடதà¯à®¤à®¿à®²à¯ {} உளà¯à®³à®®à¯ˆà®µà¯à®•à¯ கோபà¯à®ªà¯ˆ(களை) சேமிகà¯à®• விரà¯à®®à¯à®ªà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "{} உளà¯à®³à®®à¯ˆà®µà¯ கோபà¯à®ªà¯à®•à®³à¯ˆ {} இல௠சேமிகà¯à®•à®¿à®±à®¤à¯"
+
+msgid "Mirrors"
+msgstr "கணà¯à®£à®¾à®Ÿà®¿à®•à®³à¯"
+
+msgid "Mirror regions"
+msgstr "கணà¯à®£à®¾à®Ÿà®¿à®ªà¯ பகà¯à®¤à®¿à®•à®³à¯"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - அதிகபடà¯à®š மதிபà¯à®ªà¯ : {} ( {} இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯, ஒரே நேரதà¯à®¤à®¿à®²à¯ {max_downloads+1} பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯ )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "தவறான உளà¯à®³à¯€à®Ÿà¯! சரியான உளà¯à®³à¯€à®Ÿà¯à®Ÿà®¿à®²à¯ [1 à®®à¯à®¤à®²à¯ {} வரை அலà¯à®²à®¤à¯ à®®à¯à®Ÿà®•à¯à®• 0 வரை] மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯"
+
+msgid "Locales"
+msgstr "மொழி கà¯à®±à®¿à®¯à¯€à®Ÿà¯à®•à®³à¯"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®µà¯à®®à¯ (GNOME மறà¯à®±à¯à®®à¯ KDE இல௠இணையதà¯à®¤à¯ˆ வரைகலை à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ கடà¯à®Ÿà®®à¯ˆà®•à¯à®• அவசியமà¯)"
+
+msgid "Total: {} / {}"
+msgstr "மொதà¯à®¤à®®à¯: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "உளà¯à®³à®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿ அனைதà¯à®¤à¯ மதிபà¯à®ªà¯à®•à®³à¯ˆà®¯à¯à®®à¯ ஒர௠அலகà¯à®Ÿà®©à¯ பினà¯à®©à¯Šà®Ÿà¯à®Ÿà¯ இடலாமà¯: B, KB, KiB, MB, MiB..."
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "அலக௠வழஙà¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ எனிலà¯, மதிபà¯à®ªà¯ பிரிவà¯à®•à®³à®¾à®• விளகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯"
+
+msgid "Enter start (default: sector {}): "
+msgstr "தொடகà¯à®•à®¤à¯à®¤à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (இயலà¯à®ªà¯: sector {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "à®®à¯à®Ÿà®¿à®µà¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (இயலà¯à®ªà¯: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "தீரà¯à®®à®¾à®©à®¿à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ fido2 சாதனஙà¯à®•à®³à¯ˆ. libfido2 நிறà¯à®µà®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à®¾?"
+
+msgid "Path"
+msgstr "பாதை"
+
+msgid "Manufacturer"
+msgstr "உறà¯à®ªà®¤à¯à®¤à®¿à®¯à®¾à®³à®°à¯"
+
+msgid "Product"
+msgstr "தயாரிபà¯à®ªà¯"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "தவறான கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯: {error}"
+
+msgid "Type"
+msgstr "வகை"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "தொகà¯à®ªà¯à®ªà¯ பதிவிறகà¯à®•à®™à¯à®•à®³à®¿à®©à¯ போத௠à®à®±à¯à®ªà®Ÿà¯à®®à¯ இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à®¿à®©à¯ எணà¯à®£à®¿à®•à¯à®•à¯ˆà®¯à¯ˆ இநà¯à®¤ விரà¯à®ªà¯à®ªà®®à¯ செயலà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à®¤à¯"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
msgstr ""
+"இயகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯ இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à®¿à®©à¯ எணà¯à®£à®¿à®•à¯à®•à¯ˆà®¯à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯.\n"
+"\n"
+"கà¯à®±à®¿à®ªà¯à®ªà¯:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ அதிகபடà¯à®š மதிபà¯à®ªà¯: {} (ஒர௠நேரதà¯à®¤à®¿à®²à¯ {} இணையான பதிவிறகà¯à®•à®™à¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯)"
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "தொடகà¯à®•à®ªà¯ பிரிவை உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (சதவீதம௠அலà¯à®²à®¤à¯ தொகà¯à®¤à®¿ எணà¯, இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ: {}): "
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - à®®à¯à®Ÿà®•à¯à®•à¯/இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ: 0 (இணை பதிவிறகà¯à®•à®¤à¯à®¤à¯ˆ à®®à¯à®Ÿà®•à¯à®•à¯à®•à®¿à®±à®¤à¯, ஒர௠நேரதà¯à®¤à®¿à®²à¯ 1 பதிவிறகà¯à®•à®¤à¯à®¤à¯ˆ மடà¯à®Ÿà¯à®®à¯‡ அனà¯à®®à®¤à®¿à®•à¯à®•à®¿à®±à®¤à¯)\n"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "பகிரà¯à®µà®¿à®©à¯ இறà¯à®¤à®¿à®ªà¯ பகà¯à®¤à®¿à®¯à¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯ (சதவீதம௠அலà¯à®²à®¤à¯ தொகà¯à®¤à®¿ எணà¯, எ.கா: {}): "
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "தவறான உளà¯à®³à¯€à®Ÿà¯! சரியான உளà¯à®³à¯€à®Ÿà¯à®Ÿà¯à®Ÿà®©à¯ மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯ [அலà¯à®²à®¤à¯ à®®à¯à®Ÿà®•à¯à®• 0]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland கà¯à®•à¯ உஙà¯à®•à®³à¯ இரà¯à®•à¯à®•à¯ˆà®•à¯à®•à®¾à®© அணà¯à®•à®²à¯ தேவை (வனà¯à®ªà¯Šà®°à¯à®³à¯ சாதனஙà¯à®•à®³à®¿à®©à¯ சேகரிபà¯à®ªà¯ அதாவத௠விசைபà¯à®ªà®²à®•à¯ˆ, சà¯à®Ÿà¯à®Ÿà®¿ போனà¯à®±à®µà¯ˆ)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"உஙà¯à®•à®³à¯ வனà¯à®ªà¯Šà®°à¯à®³à¯à®•à¯à®•à¯ Hypland அணà¯à®•à®²à¯ˆ வழஙà¯à®•à¯à®µà®¤à®±à¯à®•à®¾à®© விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®µà¯ செயà¯à®¯à®µà¯à®®à¯"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "உளà¯à®³à®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿ அனைதà¯à®¤à¯ மதிபà¯à®ªà¯à®•à®³à¯ˆà®¯à¯à®®à¯ ஒர௠அலகà¯à®Ÿà®©à¯ பினà¯à®©à¯Šà®Ÿà¯à®Ÿà¯ இடலாமà¯: %, B, KB, KiB, MB, MiB..."
+
+msgid "Would you like to use unified kernel images?"
+msgstr "à®’à®°à¯à®™à¯à®•à®¿à®£à¯ˆà®¨à¯à®¤ கரà¯à®©à®²à¯ படஙà¯à®•à®³à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ விரà¯à®®à¯à®ªà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?"
+
+msgid "Unified kernel images"
+msgstr "à®’à®°à¯à®™à¯à®•à®¿à®£à¯ˆà®¨à¯à®¤ கரà¯à®©à®²à¯ படஙà¯à®•à®³à¯"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "நேர ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ (timedatectl show) à®®à¯à®Ÿà®¿à®µà®Ÿà¯ˆà®µà®¤à®±à¯à®•à®¾à®•à®•à¯ காதà¯à®¤à®¿à®°à¯à®•à¯à®•à®¿à®±à®¤à¯."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "நேர ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ à®®à¯à®Ÿà®¿à®µà®Ÿà¯ˆà®¯à®µà®¿à®²à¯à®²à¯ˆ, நீஙà¯à®•à®³à¯ காதà¯à®¤à®¿à®°à¯à®•à¯à®•à¯à®®à¯ போத௠- தீரà¯à®µà¯à®•à¯à®•à®¾à®© ஆவணஙà¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à®µà¯à®®à¯: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "தானியஙà¯à®•à®¿ நேர ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯à®•à¯à®•à®¾à®• காதà¯à®¤à®¿à®°à¯à®ªà¯à®ªà®¤à¯ˆà®¤à¯ தவிரà¯à®¤à¯à®¤à®²à¯ (நிறà¯à®µà®²à®¿à®©à¯ போத௠நேரம௠ஒதà¯à®¤à®¿à®šà¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà®¾à®µà®¿à®Ÿà¯à®Ÿà®¾à®²à¯ இத௠சிகà¯à®•à®²à¯à®•à®³à¯ˆ à®à®±à¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "ஆரà¯à®šà¯ லினகà¯à®¸à¯ கீரிங௠ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ (archlinux-keyring-wkd-sync) à®®à¯à®Ÿà®¿à®µà®Ÿà¯ˆà®µà®¤à®±à¯à®•à¯à®•à¯ காதà¯à®¤à®¿à®°à¯à®•à¯à®•à®¿à®±à®¤à¯."
+
+msgid "Selected profiles: "
+msgstr "தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ சà¯à®¯à®µà®¿à®µà®°à®™à¯à®•à®³à¯: "
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "நேரம௠ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ à®®à¯à®Ÿà®¿à®µà®Ÿà¯ˆà®¯à®µà®¿à®²à¯à®²à¯ˆ, நீஙà¯à®•à®³à¯ காதà¯à®¤à®¿à®°à¯à®•à¯à®•à¯à®®à¯ போத௠- தீரà¯à®µà¯à®•à®³à¯à®•à¯à®•à®¾à®© ஆவணஙà¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à®µà¯à®®à¯: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "தà¯à®µà®•à¯à®•à®•à¯à®•à¯‚டியதாகக௠கà¯à®±à®¿/கà¯à®±à®¿à®¨à¯€à®•à¯à®•à¯"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "BTRFS சà¯à®°à¯à®•à¯à®•à®¤à¯à®¤à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ விரà¯à®®à¯à®ªà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo
index 2e9d1258..2047936f 100644
--- a/archinstall/locales/tr/LC_MESSAGES/base.mo
+++ b/archinstall/locales/tr/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po
index f17efd3f..67991ec0 100644
--- a/archinstall/locales/tr/LC_MESSAGES/base.po
+++ b/archinstall/locales/tr/LC_MESSAGES/base.po
@@ -2,21 +2,21 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 21.05.2022\n"
-"PO-Revision-Date: 2022-05-25 17:42+0300\n"
+"PO-Revision-Date: 2023-12-10 20:32+0100\n"
"Last-Translator: Abdullah Koyuncu @wiseweb-works <wisewebworks@outlook.com>\n"
-"Language-Team: \n"
+"Language-Team: wiseweb-works, AlperShal, tugsatenes, eren-ince, Schwarzeisc00l, Oruch379\n"
"Language: tr\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"
-"X-Generator: Poedit 3.0.1\n"
+"X-Generator: Poedit 3.4.1\n"
msgid "[!] A log file has been created here: {} {}"
-msgstr "[!] Burada bir günlük (log) dosyası oluşturuldu: {} {}"
+msgstr "[!] Burada bir günlük dosyası oluşturuldu: {} {}"
msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
-msgstr " Lütfen bu sorunu (ve dosyayı) https://github.com/archlinux/archinstall/issues 'a bildirin"
+msgstr " Lütfen bu sorunu (ve dosyayı) https://github.com/archlinux/archinstall/issues adresine gönderin"
msgid "Do you really want to abort?"
msgstr "Gerçekten iptal etmek istiyor musunuz?"
@@ -28,13 +28,13 @@ msgid "Would you like to use swap on zram?"
msgstr "\"Swap\"i (takas) zram üzerinde kullanmak ister misiniz?"
msgid "Desired hostname for the installation: "
-msgstr "Kurulum için tercih edilen bilgisayar ismi: "
+msgstr "Kurulum için istenen ana bilgisayar adı: "
msgid "Username for required superuser with sudo privileges: "
-msgstr "Sudo yetkilerine sahip, gerekli bir süper kullanıcı için kullanıcı adı: "
+msgstr "Sudo ayrıcalıklarına sahip gerekli süper kullanıcı için kullanıcı adı: "
msgid "Any additional users to install (leave blank for no users): "
-msgstr "Kurulum için ek kullanıcılar (ek kullanıcı istemiyorsanız boş bırakın): "
+msgstr "Yüklenecek herhangi bir ek kullanıcı (kullanıcı yok için boş bırakın): "
msgid "Should this user be a superuser (sudoer)?"
msgstr "Bu kullanıcı bir süper kullanıcı (sudoer) olmalı mı?"
@@ -55,15 +55,15 @@ msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr
msgstr "Sadece base, base-devel, linux, linux-firmware, efibootmgr ve tercihi profil paketleri kuruldu."
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "Eğer bir internet tarayıcısı arzu ediyorsanız, Firefox ya da Chromium gibi, sıra gelen ekranda belirtebilirsiniz."
+msgstr "Firefox ya da Chromium gibi bir web tarayıcısı isterseniz, sıradaki ekranda belirtebilirsiniz."
msgid "Write additional packages to install (space separated, leave blank to skip): "
msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): "
msgid "Copy ISO network configuration to installation"
-msgstr "Kuruluma ISO'dan ağ yapılandırmasını kopyala"
+msgstr "ISO Ağ yapılandırmasını kuruluma kopyala"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)"
msgid "Select one network interface to configure"
@@ -91,17 +91,17 @@ msgid ""
"Select what to do with\n"
"{}"
msgstr ""
-"İle ne yapılması gerektiğini seçin\n"
-"{}"
+"{}\n"
+"ile ne yapılması gerektiğini seçin"
msgid "Enter a desired filesystem type for the partition"
msgstr "Disk bölümü için arzu edilen bir dosya systemi tipi girin"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "Başlangıç lokasyonunu girin (parted birimlerinde: s, GB, %, vb. ; default: {}): "
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "BitiÅŸ lokasyonunu girin (parted birimlerinde: s, GB, %, vb. ; ex: {}): "
msgid "{} contains queued partitions, this will remove those, are you sure?"
msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?"
@@ -122,13 +122,13 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Dizinden hangi disk bölümünün nereye mount (monte) edileceğini seçin"
+"Dizinden hangi disk bölümünün nereye mount edileceğini (bağlanacağını) seçin"
msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
-msgstr " * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır."
+msgstr " * Disk bölümü mount (bağlantı) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır."
msgid "Select where to mount partition (leave blank to remove mountpoint): "
-msgstr "Disk bölümünün nereye mount (monte) edileceğini seçin (mount noktasını kaldırmak için boş bırakın): "
+msgstr "Disk bölümünün nereye mount edileceğini (bağlanacağını) seçin (mount (bağlantı) noktasını kaldırmak için boş bırakın): "
msgid ""
"{}\n"
@@ -155,7 +155,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"Hangi disk bölümünün boot edilebilir olarak işaretleneceğini seçin"
+"Hangi disk bölümünün önyüklenebilir olarak işaretleneceğini seçin"
msgid ""
"{}\n"
@@ -173,7 +173,7 @@ msgid "Archinstall language"
msgstr "Archinstall dili"
msgid "Wipe all selected drives and use a best-effort default partition layout"
-msgstr "Bütün seçilmiş diskleri temizle ve elden gelen en iyi varsayılan disk bölümü düzenini kullan"
+msgstr "Bütün seçilmiş diskleri temizle ve olabilecek en iyi varsayılan disk bölümü düzenini kullan"
msgid "Select what to do with each individual drive (followed by partition usage)"
msgstr "Her bir disk ile ne yapılacağını seçin (disk bölümü kullanımı ile takip edilir)"
@@ -200,7 +200,7 @@ msgid "For the best compatibility with your Intel hardware, you may want to use
msgstr "Intel donanımınızla en iyi uyumluluk için, tam açık-kaynak ya da Intel ayarlarından birini kullanmak isteyebilirsiniz.\n"
msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
-msgstr "Nvidia donanımınızla en iyi uyumluluk için, Nvidia patentli sürücüyü kullanmak isteyebilirsiniz.\n"
+msgstr "Nvidia donanımınızla en iyi uyumluluk için, Nvidia sahipli sürücüyü kullanmak isteyebilirsiniz.\n"
msgid ""
"\n"
@@ -217,15 +217,12 @@ msgstr "Tam açık-kaynak (varsayılan)"
msgid "Choose which kernels to use or leave blank for default \"{}\""
msgstr "Hangi çekirdekleri kullanmak istediğinizi seçin ya da varsayılan \"{}\" için boş bırakın"
-# Burada "locale" hesaba özgü yani profil içinde yerel anlamına geliyor olabilir. Kurulumda ilgili metin ile karşılaşmadığımdan karar veremiyorum.
msgid "Choose which locale language to use"
-msgstr "Hangi dilin kullanılacağını seçin"
+msgstr "Hangi yerel ayar dilinin kullanılacağını seçin"
-# Burada "locale" hesaba özgü yani profil içinde yerel anlamına geliyor olabilir. Kurulumda ilgili metin ile karşılaşmadığımdan karar veremiyorum.
msgid "Choose which locale encoding to use"
-msgstr "Hangi yerel dil kod şemasının kullanılacağını seçin"
+msgstr "Hangi yerel ayar kod şemasının kullanılacağını seçin"
-# Burada "locale" hesaba özgü yani profil içinde yerel anlamına geliyor olabilir. Kurulumda ilgili metin ile karşılaşmadığımdan karar veremiyorum.
msgid "Select one of the values shown below: "
msgstr "Aşağıda gösterilen değerlerden birini seçin: "
@@ -250,13 +247,11 @@ msgstr "Klavye düzeni"
msgid "Mirror region"
msgstr "İndirme sunucusu bölgesi"
-# Burada "locale" hesaba özgü yani profil içinde yerel anlamına geliyor olabilir. Kurulumda ilgili metin ile karşılaşmadığımdan karar veremiyorum.
msgid "Locale language"
-msgstr "Yerel dil"
+msgstr "Yerel ayar dili"
-# Burada "locale" hesaba özgü yani profil içinde yerel anlamına geliyor olabilir. Kurulumda ilgili metin ile karşılaşmadığımdan karar veremiyorum.
msgid "Locale encoding"
-msgstr "Yerel dil kod şeması"
+msgstr "Yerel ayar dil kod şeması"
msgid "Drive(s)"
msgstr "Disk(ler)"
@@ -264,11 +259,12 @@ msgstr "Disk(ler)"
msgid "Disk layout"
msgstr "Disk şeması"
+# Şifreleme şifresi bana bir tık garip geldi, o yüzden parolayla değiştirdim
msgid "Encryption password"
-msgstr "Åžifreleme ÅŸifresi"
+msgstr "Şifreleme parolası"
msgid "Swap"
-msgstr "Swap (deÄŸiÅŸim)"
+msgstr "Swap (takas)"
msgid "Bootloader"
msgstr "Önyükleyici"
@@ -310,7 +306,7 @@ msgid ""
"Do you wish to continue?"
msgstr ""
"Sabit disk seçimini geçmeye karar verdiniz ve\n"
-"{}'e ne mount (monte) edilmişse onu kullanacaksınız (deneysel)\n"
+"{}'e ne mount edilmişse (bağlanmışsa) onu kullanacaksınız (deneysel)\n"
"UYARI: Archinstall bu kurulumun uygunluÄŸunu kontrol etmeyecek\n"
"Devam etmek istiyor musunuz?"
@@ -327,7 +323,7 @@ msgid "Clear/Delete all partitions"
msgstr "Bütün disk bölümlerini temizle/sil"
msgid "Assign mount-point for a partition"
-msgstr "Bir disk bölümü için mount (monte) noktası ata"
+msgstr "Bir disk bölümü için mount (bağlantı) noktası ata"
msgid "Mark/Unmark a partition to be formatted (wipes data)"
msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)"
@@ -336,10 +332,10 @@ msgid "Mark/Unmark a partition as encrypted"
msgstr "Bir disk bölümünü şifrelenmiş olarak işaretle/işareti kaldır"
msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
-msgstr "Bir disk bölümünü boot edilebilir olarak işaretle/işareti kaldır (/boot için otomatik)"
+msgstr "Bir disk bölümünü önyüklenebilir olarak işaretle/işareti kaldır (/boot için otomatik)"
msgid "Set desired filesystem for a partition"
-msgstr "Bir disk bölümü için arzu edilen dosya sistemini ayarla"
+msgstr "Bir disk bölümü için istenilen dosya sistemini ayarla"
msgid "Abort"
msgstr "Ä°ptal et"
@@ -354,7 +350,7 @@ msgid "Timezone"
msgstr "Zaman dilimi"
msgid "Set/Modify the below options"
-msgstr "Aşağıdaki ayarları ayarla/modifiye et"
+msgstr "Aşağıdaki ayarları ayarla/değiştir"
msgid "Install"
msgstr "Kur"
@@ -370,17 +366,18 @@ msgstr "Disk bölümü şeması öner"
msgid "Enter a password: "
msgstr "Bir ÅŸifre girin: "
+# ÅŸifreleme ÅŸifresi tam olmuyor sanki, deÄŸiÅŸtirdim
msgid "Enter a encryption password for {}"
-msgstr "{} için bir şifreleme şifresi girin"
+msgstr "{} için bir şifreleme parolası girin"
msgid "Enter disk encryption password (leave blank for no encryption): "
-msgstr "Disk şifreleme şifresi girin (şifreleme olmaması için boş bırakın): "
+msgstr "Disk şifreleme parolası girin (şifreleme olmaması için boş bırakın): "
msgid "Create a required super-user with sudo privileges: "
msgstr "Gerekli bir sudo yetkilerine sahip süper-kullanıcı oluşturun: "
msgid "Enter root password (leave blank to disable root): "
-msgstr "Root (kök) şifresi girin (root'u hizmet dışı bırakmak için boş bırakın): "
+msgstr "Root (kök) şifresi girin (root'u devre dışı bırakmak için boş bırakın): "
msgid "Password for user \"{}\": "
msgstr "Kullanıcı \"{}\" için şifre: "
@@ -389,7 +386,7 @@ msgid "Verifying that additional packages exist (this might take a few seconds)"
msgstr "Ek paketlerin varlığı doğrulanıyor (bu bir kaç saniye alabilir)"
msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
-msgstr "Varsayılan zaman sunucularıyla otomatik zaman eşzamanlamasını (NTP) kullanmak ister misiniz?\n"
+msgstr "Varsayılan zaman sunucularıyla otomatik zaman eş zamanlamasını (NTP) kullanmak ister misiniz?\n"
msgid ""
"Hardware time and other post-configuration steps might be required in order for NTP to work.\n"
@@ -449,10 +446,10 @@ msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate
msgstr "Pacman hâlihazırda çalışıyor, işlemin sonlandırılması için en fazla 10 dakika beklenecek."
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "Önceden var olan pacman kilidi çıkış yapmadı. Lütfen archinstall'ı kullanmadan once mevcut olan pacman oturumlarını temizleyin."
+msgstr "Önceden var olan pacman kilidi çıkış yapmadı. Lütfen archinstall'u kullanmadan önce mevcut olan pacman oturumlarını temizleyin."
msgid "Choose which optional additional repositories to enable"
-msgstr "Hangi tercihi ek repositorylerin (depoların) aktifleştirileceğini seçin"
+msgstr "Hangi tercihi ek depoların (repositorylerin) aktifleştirileceğini seçin"
msgid "Add a user"
msgstr "Kullanıcı ekle"
@@ -461,10 +458,10 @@ msgid "Change password"
msgstr "Åžifre deÄŸiÅŸtir"
msgid "Promote/Demote user"
-msgstr "Kullanıcı terfi ettir/indirge"
+msgstr "Kullanıcıyı terfi et/indirge"
msgid "Delete User"
-msgstr "Kullanıcı Sil"
+msgstr "Kullanıcıyı Sil"
msgid ""
"\n"
@@ -477,7 +474,7 @@ msgid "User Name : "
msgstr "Kullanıcı Adı : "
msgid "Should {} be a superuser (sudoer)?"
-msgstr "{} bir süper kullanıcı (sudoer) olmalı mı?"
+msgstr "{} bir süper kullanıcı (sudoer) mı olmalı?"
msgid "Define users with sudo privilege: "
msgstr "Sudo yetkilerine sahip kullanıcıları tanımlayın: "
@@ -486,7 +483,7 @@ msgid "No network configuration"
msgstr "Ağ yapılandırması yok"
msgid "Set desired subvolumes on a btrfs partition"
-msgstr "Btrfs disk bölümünde arzu edilen alt disk bölümülerini ayarlayın"
+msgstr "Btrfs disk bölümünde istenilen alt disk bölümülerini ayarlayın"
msgid ""
"{}\n"
@@ -525,22 +522,22 @@ msgid "Not a valid directory: {}"
msgstr "Geçerli bir dizin değil: {}"
msgid "The password you are using seems to be weak,"
-msgstr "Kullandığınız şifre zayıf gözüküyor,"
+msgstr "Kullandığınız şifre zayıf görünüyor,"
msgid "are you sure you want to use it?"
msgstr "kullanmak istediÄŸinize emin misiniz?"
msgid "Optional repositories"
-msgstr "Tercihi \"repository\"ler (depolar)"
+msgstr "Tercihi depolar"
msgid "Save configuration"
-msgstr "Yapılandırmayı kaydet"
+msgstr "Konfigürasyonu kaydet"
msgid "Missing configurations:\n"
-msgstr "Eksik yapılandırmalar:\n"
+msgstr "Eksik konfigürasyon:\n"
msgid "Either root-password or at least 1 superuser must be specified"
-msgstr "Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda"
+msgstr "Kök (yönetici) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda"
msgid "Manage superuser accounts: "
msgstr "Süper-kullanıcı hesaplarını yönet: "
@@ -552,7 +549,7 @@ msgid " Subvolume :{:16}"
msgstr " alt disk bölümü :{:16}"
msgid " mounted at {:16}"
-msgstr " {:16}'da mount (monte) edildi"
+msgstr " {:16}'da monte edildi"
msgid " with option {}"
msgstr " {} seçeneğiyle"
@@ -562,16 +559,16 @@ msgid ""
" Fill the desired values for a new subvolume \n"
msgstr ""
"\n"
-"Yeni bir alt disk bölümü için arzu edilen değerleri doldurun\n"
+"Yeni bir alt disk bölümü için istenilen değerleri doldurun\n"
msgid "Subvolume name "
msgstr "Alt disk bölümü ismi "
msgid "Subvolume mountpoint"
-msgstr "Alt disk bölümü mount (monte) noktası"
+msgstr "Alt disk bölümü bağlantı noktası"
msgid "Subvolume options"
-msgstr "Alt dizin bölümü seçenekleri"
+msgstr "Alt disk bölümü seçenekleri"
msgid "Save"
msgstr "Kaydet"
@@ -580,16 +577,16 @@ msgid "Subvolume name :"
msgstr "Alt disk bölümü ismi :"
msgid "Select a mount point :"
-msgstr "Bir mount (monte) noktası seçin :"
+msgstr "Bir bağlantı noktası seçin :"
msgid "Select the desired subvolume options "
-msgstr "İstenilen alt dizin bölümü ayarlarını seçin "
+msgstr "İstenilen alt disk bölümü ayarlarını seçin "
msgid "Define users with sudo privilege, by username: "
-msgstr "Sudo yetkilerine sahip kullanıcıları tanımlayın, kullanıcı adı ile: "
+msgstr "Süper-kullanıcı yetkilerine sahip kullanıcıları tanımlayın, kullanıcı adı ile: "
msgid "[!] A log file has been created here: {}"
-msgstr "[!] Burada bir günlük (log) dosyası oluşturuldu: {}"
+msgstr "[!] Burada bir günlük dosyası oluşturuldu: {}"
msgid "Would you like to use BTRFS subvolumes with a default structure?"
msgstr "BTRFS alt disk bölümlerini varsayılan yapıyla kullanmak ister misiniz?"
@@ -649,21 +646,20 @@ msgid "Mark/Unmark a partition as compressed (btrfs only)"
msgstr "Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kaldır (sadece btrfs)"
msgid "The password you are using seems to be weak, are you sure you want to use it?"
-msgstr "Kullandığınız şifre zayıf gözüküyor, kullanmak istediğinize emin misiniz?"
+msgstr "Kullandığınız şifre zayıf görünüyor, kullanmak istediğinize emin misiniz?"
msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
-msgstr "Masaüstü ortamları ve otomatik hizalamalı pencere yöneticilerine bir seçenek sunar, örnek olarak gnome, kde, sway"
+msgstr "Masaüstü ortamları ve otomatik döşemeli pencere yöneticilerine bir seçenek sunar, örnek olarak gnome, kde, sway"
msgid "Select your desired desktop environment"
-msgstr "Arzu ettiğiniz masaüstü ortamını seçin"
+msgstr "İstediğiniz masaüstü ortamını seçin"
msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
-msgstr "Arch Linux'u istediğin gibi kişiselleştirmeni sağlayan çok basit bir kurulum."
+msgstr "Arch Linux'u uygun gördüğünüz şekilde özelleştirmenize olanak tanıyan çok basit bir kurulum."
msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
msgstr "Kurmak ve aktif etmek için bazı seçilmiş çeşitli sunucu paketleri sunar, örnek olarak httpd, nginx, mariadb"
-#, fuzzy
msgid "Choose which servers to install, if none then a minimal installation will be done"
msgstr "Hangi sunucuların kurulacağını seçin, eğer hiçbiri seçilmezse minimal bir kurulum gerçekleştirilecektir"
@@ -674,7 +670,7 @@ msgid "Press Enter to continue."
msgstr "Devam etmek için Enter'a bas."
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "Yeni oluşturulmuş kuruluma chroot etmek ve kurulum sonrası konfigürasyon gerçekleştirmek ister misiniz?"
+msgstr "Yeni oluşturulan kuruluma chroot ile girmek ve kurulum sonrası yapılandırmayı gerçekleştirmek ister misiniz?"
msgid "Are you sure you want to reset this setting?"
msgstr "Bu ayarı sıfırlamak istediğinize emin misiniz?"
@@ -683,7 +679,7 @@ msgid "Select one or more hard drives to use and configure\n"
msgstr "Kullanmak ve yapılandırmak için bir ya da daha fazla sabit disk seçin\n"
msgid "Any modifications to the existing setting will reset the disk layout!"
-msgstr "Mevcut ayara herhangi bir modifikasyon disk şemasını sıfırlayacaktır!"
+msgstr "Mevcut ayarda yapılacak herhangi bir değişiklik disk düzenini sıfırlayacaktır!"
msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
msgstr "Eğer sabit disk seçimini sıfırlarsanız bu ayrıca mevcut disk şemasını da sıfırlayacaktır. Emin misiniz?"
@@ -733,7 +729,7 @@ msgid "Value: "
msgstr "DeÄŸer: "
msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
-msgstr "Disk seçmeyi ve disk bölümlendirmeyi geçebilir ve disk kurulumu /mnt dizininde neye mount (monte) ediliyse onu kullanabilirsiniz (deneysel)"
+msgstr "Disk seçmeyi ve disk bölümlendirmeyi geçebilir ve disk kurulumu /mnt dizininde neye mount ediliyse (bağlandıysa) onu kullanabilirsiniz (deneysel)"
msgid "Select one of the disks or skip and use /mnt as default"
msgstr "Disklerden birini seçin ya da geçin ve /mnt dizinini varsayılan olarak kullanın"
@@ -756,135 +752,562 @@ msgstr "BoÅŸ alan"
msgid "Bus-type"
msgstr "Bus(veri yolu)-tipi"
-#, fuzzy
msgid "Either root-password or at least 1 user with sudo privileges must be specified"
-msgstr "Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda"
+msgstr "Kök şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda"
-#, fuzzy
msgid "Enter username (leave blank to skip): "
msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): "
msgid "The username you entered is invalid. Try again"
-msgstr ""
+msgstr "Girdiğiniz kullanıcı adı geçersiz. Tekrar deneyin"
-#, fuzzy
msgid "Should \"{}\" be a superuser (sudo)?"
msgstr "{} bir süper kullanıcı (sudoer) olmalı mı?"
-#, fuzzy
msgid "Select which partitions to encrypt"
-msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin"
+msgstr "Hangi disk bölümünün şifrelemek için işaretleneceğini seçin"
msgid "very weak"
-msgstr ""
+msgstr "çok zayıf"
msgid "weak"
-msgstr ""
+msgstr "zayıf"
msgid "moderate"
-msgstr ""
+msgstr "ortalama"
msgid "strong"
-msgstr ""
+msgstr "güçlü"
-#, fuzzy
msgid "Add subvolume"
-msgstr " alt disk bölümü :{:16}"
+msgstr "Alt disk bölümü ekle"
msgid "Edit subvolume"
-msgstr ""
+msgstr "Alt disk bölümünü düzenle"
-#, fuzzy
msgid "Delete subvolume"
-msgstr "Kullanıcı Sil"
+msgstr "Alt disk bölümü sil"
msgid "Configured {} interfaces"
-msgstr ""
+msgstr "Yapılandırılmış {} arayüzler"
msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr ""
+msgstr "Bu seçenek, yükleme sırasında meydana gelebilecek paralel indirme sayısını etkinleştirir"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
+"Etkinleştirilecek paralel indirme sayısını girin.\n"
+" (1 ile {max_downloads} arasında bir değer girin)\n"
+"Not:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr ""
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - Maksimum deÄŸer : {} ( {} paralel indirmeye izin verir, bir seferde {} indirmeye izin verir)"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr ""
+msgstr " - Minimum deÄŸer : 1 ( 1 paralel indirmeye izin verir, bir seferde 2 indirmeye izin verir )"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr ""
+msgstr " - Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, aynı anda yalnızca 1 indirmeye izin verir)"
#, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr ""
+msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [{max_downloads} için 1, veya devre dışı bırakmak için 0]"
msgid "Parallel Downloads"
-msgstr ""
+msgstr "Paralel Ä°ndirmeler"
-#, fuzzy
msgid "ESC to skip"
-msgstr "Geçmek için ESC'yi kullanın"
+msgstr "Geçmek için ESC'ye basın"
msgid "CTRL+C to reset"
-msgstr ""
+msgstr "Sıfırlamak için CTRL+C"
msgid "TAB to select"
-msgstr ""
+msgstr "Seçmek için TAB"
msgid "[Default value: 0] > "
-msgstr ""
+msgstr "[Varsayılan değer: 0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr ""
+msgstr "Bu çeviriyi kullanabilmek için lütfen dili destekleyen bir yazı tipini manuel olarak yükleyin."
msgid "The font should be stored as {}"
-msgstr ""
+msgstr "Yazı tipi {} olarak saklanmalıdır."
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall'un çalışması için kök ayrıcalıkları gerekir. Daha fazla bilgi için --help'e bakın."
+
+msgid "Select an execution mode"
+msgstr "Bir çalıştırma modu seçin"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "Belirtilen url'den profil getirilemiyor: {}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "Profiller benzersiz ada sahip olmalıdır, ancak yinelenen ada sahip profil tanımları bulundu: {}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "Kullanılacak ve yapılandırılacak bir veya daha fazla cihaz seçin"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Aygıt seçimini sıfırlarsanız, bu aynı zamanda geçerli disk düzenini de sıfırlayacaktır. Emin misiniz?"
+
+msgid "Existing Partitions"
+msgstr "Mevcut Bölmeler"
+
+msgid "Select a partitioning option"
+msgstr "Bir bölümleme seçeneği belirleyin"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Takılı aygıtların kök dizinini girin: "
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "/home disk bölümü için minimum kapasite: {}GiB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Arch Linux disk bölümü için minimum kapasite: {}GiB"
+
+# “profiles_bck†bir yer tutucu olabilir. O yuzden sabit tuttum.
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Bu, önceden programlanmış profiles_bck listesidir, masaüstü ortamları gibi şeyleri yüklemeyi kolaylaştırabilirler"
+
+msgid "Current profile selection"
+msgstr "Mevcut disk bölümü düzeni"
+
+msgid "Remove all newly added partitions"
+msgstr "Yeni eklenen tüm disk bölümleri kaldırın"
+
+msgid "Assign mountpoint"
+msgstr "Bir disk bölümü için mount (bağlantı) noktası ata"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (verileri siler)"
+
+msgid "Mark/Unmark as bootable"
+msgstr "Önyüklenebilir olarak işaretle/işareti kaldır"
+
+msgid "Change filesystem"
+msgstr "Dosya sistemini deÄŸiÅŸtir"
+
+msgid "Mark/Unmark as compressed"
+msgstr "Sıkıştırıldı olarak işaretle/işareti kaldır"
+
+msgid "Set subvolumes"
+msgstr "Alt disk bölümü ayarla"
+
+msgid "Delete partition"
+msgstr "Disk bölümü sil"
+
+msgid "Partition"
+msgstr "Disk bölümü"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "Bu disk bölümü şu anda şifrelenmiştir, biçimlendirmek için bir dosya sistemi belirtilmelidir"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "Bölüm bağlama noktaları kurulumun içine görelidir, örnek olarak önyükleme /boot olacaktır."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "Mountpoint /boot ayarlanmışsa, bölüm de önyüklenebilir olarak işaretlenecektir."
+
+msgid "Mountpoint: "
+msgstr "Bağlantı noktası: "
+
+msgid "Current free sectors on device {}:"
+msgstr "{} cihazındaki mevcut boş sektörler:"
+
+msgid "Total sectors: {}"
+msgstr "Toplam sektörler: {}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "Başlangıç sektörünü girin (varsayılan: {}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Bölümün bitiş sektörünü girin (yüzde veya blok numarası, varsayılan: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "Bu işlem yeni eklenmiş tüm bölümleri kaldıracaktır, devam edilsin mi?"
+
+msgid "Partition management: {}"
+msgstr "Bölüm yönetimi: {}"
+
+msgid "Total length: {}"
+msgstr "Toplam uzunluk: {}"
-#, fuzzy
msgid "Encryption type"
-msgstr "Åžifreleme ÅŸifresi"
+msgstr "Şifreleme türü"
msgid "Partitions"
-msgstr ""
+msgstr "Disk bölümleri"
msgid "No HSM devices available"
-msgstr ""
+msgstr "Kullanılabilir HSM cihazı yok"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin"
+msgstr "Şifrelenecek bölümler"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "Disk şifreleme seçeneğini seçin"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "HSM için kullanılacak bir FIDO2 cihazı seçin"
+
+msgid "Use a best-effort default partition layout"
+msgstr "Olabilecek en iyi varsayılan bölüm düzeni kullanın"
+
+msgid "Manual Partitioning"
+msgstr "Manuel yapılandırma"
+
+msgid "Pre-mounted configuration"
+msgstr "Önceden mount edilmiş (bağlanmış) konfigürasyon"
+
+msgid "Unknown"
+msgstr "Bilinmeyen"
+
+msgid "Partition encryption"
+msgstr "Disk bölümü şifrelemesi"
+
+msgid " ! Formatting {} in "
+msgstr " ! {} biçimlendiriliyor "
+
+msgid "↠Back"
+msgstr "↠Geri"
+
+msgid "Disk encryption"
+msgstr "Disk ÅŸifrelemesi"
+
+msgid "Configuration"
+msgstr "Konfigürasyon"
+
+msgid "Password"
+msgstr "Åžifre"
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?"
+msgstr "Bütün ayarlar sıfırlanacak, emin misiniz?"
msgid "Back"
+msgstr "Geri"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "Lütfen seçilen profiller için hangi karşılayıcının kurulacağını seçin: {}"
+
+msgid "Environment type: {}"
+msgstr "Ortam türü: {}"
+
+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 "Tescilli Nvidia sürücüsü Sway tarafından desteklenmiyor. Sorunlarla karşılaşmanız muhtemeldir, bu sizin için uygun mu?"
+
+msgid "Installed packages"
+msgstr "Yüklü paketler"
+
+msgid "Add profile"
+msgstr "Profil ekle"
+
+msgid "Edit profile"
+msgstr "Profil düzenle"
+
+msgid "Delete profile"
+msgstr "Profil sil"
+
+msgid "Profile name: "
+msgstr "Profil ismi: "
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Girdiğiniz profil adı zaten kullanılıyor. Tekrar deneyin"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): "
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Bu profille etkinleştirilecek hizmetler (boşluk bırakılmış, atlamak için boş bırakın): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr "Bu profil kurulum için etkinleştirilmeli mi?"
+
+msgid "Create your own"
+msgstr "Kendinizinkini oluÅŸturun"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"Bir grafik sürücüsü seçin veya tüm açık kaynak sürücülerini yüklemek için boş bırakın"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway'in seat'e erişmesi gerekir (klavye, fare vb. donanım aygıtlarının birleşimi)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"Sway'in donanımınıza erişmesine izin vermek için bir seçenek belirleyin"
+
+msgid "Graphics driver"
+msgstr "Grafik sürücüsü"
+
+msgid "Greeter"
+msgstr "Karşılayıcı"
+
+msgid "Please chose which greeter to install"
+msgstr "Lütfen hangi karşılayıcının kurulacağını seçin"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "Bu, önceden programlanmış default_profiles listesidir"
+
+msgid "Disk configuration"
+msgstr "Disk yapılandırması"
+
+msgid "Profiles"
+msgstr "Profiller"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "Yapılandırma dosyalarını kaydetmek için olası dizinler aranıyor..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Yapılandırma dosyalarını kaydetmek için dizin (veya dizinler) seçin"
+
+msgid "Add a custom mirror"
+msgstr "Özel indirme sunucusu ekleyin"
+
+msgid "Change custom mirror"
+msgstr "Özel indirme sunucusunu değiştirin"
+
+msgid "Delete custom mirror"
+msgstr "Özel indirme sunucusunu silin"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "İsim girin (geçmek için boş bırakın): "
+
+msgid "Enter url (leave blank to skip): "
+msgstr "Url girin (geçmek için boş bırakın): "
+
+msgid "Select signature check option"
+msgstr "İmza kontrolü seçeneğini seçin"
+
+msgid "Select signature option"
+msgstr "İmza seçeneğini seçin"
+
+msgid "Custom mirrors"
+msgstr "Kişisel depo aynası"
+
+msgid "Defined"
+msgstr "Tanımlı"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "Kullanıcı konfigürasyonunu kaydet (Disk düzeni ile birlikte)"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"Kaydedilecek yapılandırma(lar) için bir dizin girin (sekme tamamlama etkin)\n"
+"Dizini kaydet: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr "{} konfigürasyon dosya(lar)ını gösterilen konuma kaydetmek ister miydiniz?"
+
+msgid "Saving {} configuration files to {}"
+msgstr "{} konfigürasyon dosyaları {} konumuna kaydediliyor"
+
+msgid "Mirrors"
+msgstr "Ayna sunucular"
+
+msgid "Mirror regions"
+msgstr "Ayna sunucusu bölgesi"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " Maximum deÄŸer : {} ( {} paralel indirmelerine izin verir, Tek seferde {max_download+1} indirmeye izin verir.)"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]"
+
+msgid "Locales"
+msgstr "Yerel Ayarlar"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)"
+
+msgid "Total: {} / {}"
+msgstr "Toplam: {} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "Girilen tüm değerlerin sonuna bir birim eklenebilir: B, KB, KiB, MB, MiB…"
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "Herhangi bir birim belirtilmezse, değer sektörler olarak yorumlanır"
+
+msgid "Enter start (default: sector {}): "
+msgstr "Başlangıç girin (varsayılan: sektör {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "Bitiş girin (varsayılan: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "Fido2 cihazları belirlenemiyor. libfido2 yüklü mü?"
+
+msgid "Path"
+msgstr "Yol/Konum"
+
+msgid "Manufacturer"
+msgstr "Ãœretici firma"
+
+msgid "Product"
+msgstr "Ürün"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Geçersiz yapılandırma: {error}"
+
+msgid "Type"
+msgstr "Tür"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Bu seçenek, paket indirmeleri sırasında oluşabilecek paralel indirme sayısını etkinleştirir"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Etkinleştirilecek paralel indirme sayısını girin.\n"
+"\n"
+"Not:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - Önerilen maksimum değer : {} ( Bir seferde {} paralel indirmeye izin verir )"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Devre Dışı / Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, bir seferde yalnızca 1 indirmeye izin verir)\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Geçersiz girdi! Geçerli bir girişle [veya devre dışı bırakmak için 0 ile] tekrar deneyin"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland’in donanımlarınıza erişmesi gerekir (klavye, fare vb. donanım aygıtlarının toplanması)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
msgstr ""
+"\n"
+"\n"
+"Hyprland’e donanımınıza erişim izni vermek için bir seçenek belirleyin"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "Girilen tüm değerlerin sonuna bir birim eklenebilir: %, B, KB, KiB, MB, MiB…"
+
+msgid "Would you like to use unified kernel images?"
+msgstr "Birleştirilmiş çekirdek görüntülerini (Unified kernel) kullanmak ister misiniz?"
+
+msgid "Unified kernel images"
+msgstr "Birleşik çekirdek görüntüleri (Unified kernels)"
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr "Zaman senkronizasyonunun (timedatectl show) tamamlanması bekleniyor."
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Siz beklerken zaman senkronizasyonu tamamlanmıyor - geçici çözümler için dokümanları kontrol edin: https://archinstall.readthedocs.io/"
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr "Otomatik zaman senkronizasyonunu beklemenin atlanması (kurulum sırasında zamanın senkronize olmaması durumunda bu durum sorunlara neden olabilir)"
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr "Arch Linux anahtarlık senkronizasyonunun (archlinux-keyring-wkd-sync) tamamlanması bekleniyor."
#, fuzzy
-msgid "Password"
-msgstr "Root (kök) şifresi"
+msgid "Selected profiles: "
+msgstr "Profil sil"
-msgid "Partition encryption"
+#, fuzzy
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr "Siz beklerken zaman senkronizasyonu tamamlanmıyor - geçici çözümler için dokümanları kontrol edin: https://archinstall.readthedocs.io/"
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Önyüklenebilir olarak işaretle/işareti kaldır"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "BTRFS sıkıştırmasını kullanmak ister misiniz?"
+
+msgid "Use compression"
+msgstr "Sıkıştırma kullan"
+
+msgid "Disable Copy-on-Write"
+msgstr "Copy-on-write'ı devre dışı bırak"
+
+#, fuzzy
+msgid "Partitioning"
+msgstr "Disk bölümü"
+
+msgid "Logical Volume Management (LVM)"
+msgstr "Mantıksal Hacim Yönetimi (LVM)"
+
+msgid "Physical volumes"
+msgstr "Fiziksel birimler"
+
+msgid "Volumes"
+msgstr "Birimler"
+
+#, fuzzy
+msgid "Default layout"
+msgstr "Varsayılan düzen"
+
+#, fuzzy
+msgid "No Encryption"
+msgstr "Åžifreleme yok"
+
+msgid "LUKS"
+msgstr ""
+
+msgid "LVM on LUKS"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): "
+msgid "LUKS on LVM"
+msgstr ""
+
+#, fuzzy
+msgid "LVM volumes"
+msgstr "Alt disk bölümü ayarla"
+
+#, fuzzy
+msgid "LVM volumes to be encrypted"
+msgstr "Şifrelenecek bölümler"
+
+#, fuzzy
+msgid "Select which LVM volumes to encrypt"
+msgstr "Hangi disk bölümünün şifrelemek için işaretleneceğini seçin"
+
+#, fuzzy
+msgid "Configuration type: {}"
+msgstr "Konfigürasyon tipi: {}"
+
+#, fuzzy
+msgid "LVM configuration type"
+msgstr "LVM yapılandırma türü"
+
+msgid "LVM disk encryption with more than 2 partitions is currently not supported"
+msgstr "Şu anda 2'den fazla bölüm içeren LVM disk şifrelemesi desteklenmemektedir"
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE Plasma)"
+msgstr "NetworkManager'ı kullan (GNOME ya da KDE Plasma'da interneti grafik olarak yapılandırmak için gerekli)"
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): "
+#, fuzzy
+msgid "Select a LVM option"
+msgstr "bir LVM seçeneği seçin."
+
+#, fuzzy
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. GNOME, KDE Plasma, Sway"
+msgstr "Masaüstü ortamları ve otomatik döşemeli pencere yöneticilerine bir seçenek sunar, örnek olarak GNOME, KDE Plasma, Sway"
diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo
index ae98dcfb..8c8cfcd3 100644
--- a/archinstall/locales/uk/LC_MESSAGES/base.mo
+++ b/archinstall/locales/uk/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po
index ee9740fc..b000adbd 100644
--- a/archinstall/locales/uk/LC_MESSAGES/base.po
+++ b/archinstall/locales/uk/LC_MESSAGES/base.po
@@ -62,7 +62,7 @@ msgstr "Ðапишіть додаткові пакети Ð´Ð»Ñ Ñ–Ð½ÑталÑц
msgid "Copy ISO network configuration to installation"
msgstr "Скопіюйте конфігурацію мережі ISO Ð´Ð»Ñ Ð²ÑтановленнÑ"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "ВикориÑтовувати NetworkManager (необхідний Ð´Ð»Ñ Ð³Ñ€Ð°Ñ„Ñ–Ñ‡Ð½Ð¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð†Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚Ñƒ в GNOME та KDE)"
msgid "Select one network interface to configure"
@@ -793,18 +793,17 @@ msgstr "Ðалаштовані інтерфейÑи {}"
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr "Цей параметр вмикає кількіÑÑ‚ÑŒ паралельних завантажень, Ñкі можуть відбуватиÑÑ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ Ð²ÑтановленнÑ"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"Введіть кількіÑÑ‚ÑŒ паралельних завантажень, Ñкі потрібно ввімкнути.\n"
-" (Введіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ 1 до {max_downloads})\n"
+" (Введіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ 1 до {})\n"
"Примітка:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - МакÑимальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ : {max_downloads} ( ДозволÑÑ” {max_downloads} паралельних завантажень, дозволÑÑ” {max_downloads+1} завантажень за раз )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - МакÑимальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ : {} ( ДозволÑÑ” {} паралельних завантажень, дозволÑÑ” {} завантажень за раз )"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
msgstr " - Мінімальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ : 1 ( дозволÑÑ” 1 паралельне завантаженнÑ, дозволÑÑ” 2 Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¾Ð´Ð½Ð¾Ñ‡Ð°Ñно )"
@@ -812,9 +811,9 @@ msgstr " - Мінімальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ : 1 ( дозволÑÑ” 1 па
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
msgstr " - Вимкнути/Типово : 0 ( Вимикає паралельне завантаженнÑ, дозволÑÑ” лише 1 Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð° раз )"
-#, python-brace-format
+#, fuzzy, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "Ðекоректне введеннÑ! Повторіть Ñпробу з валідним введеннÑм [від 1 до {max_downloads} або 0 Ð´Ð»Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ]"
+msgstr "Ðекоректне введеннÑ! Повторіть Ñпробу з валідним введеннÑм [від 1 до {} або 0 Ð´Ð»Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ]"
msgid "Parallel Downloads"
msgstr "Паралельні ЗавантаженнÑ"
@@ -837,6 +836,126 @@ msgstr "Щоб мати можливіÑÑ‚ÑŒ викориÑтовувати це
msgid "The font should be stored as {}"
msgstr "Шрифт Ñлід зберігати Ñк {}"
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "Оберіть дію Ð´Ð»Ñ \"{}\""
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "Оберіть один або кілька жорÑтких диÑків Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð° налаштуваннÑ"
+
+#, fuzzy
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "Якщо ви Ñкинете вибір жорÑткого диÑка, це також Ñкине поточну Ñхему розмітки диÑка. Ви впевнені?"
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "Розділи"
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "Оберіть параметр ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¸Ñка"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "Введіть каталог Ð´Ð»Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ð¹, Ñкі потрібно зберегти: "
+
+#, fuzzy
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "Мінімальна ємніÑÑ‚ÑŒ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñƒ /home: {} Гб\n"
+
+#, fuzzy
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Мінімальна ємніÑÑ‚ÑŒ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñƒ Arch Linux: {} Гб"
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "Це ÑпиÑок попередньо запрограмованих профілів, вони можуть ÑпроÑтити вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð°ÐºÐ¸Ñ… речей, Ñк Ñередовища робочого Ñтолу"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "Поточна Ñхема розмітки розділу"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "Створіть новий розділ"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "Призначити точку Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñƒ"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Позначити/знÑти позначку з розділу Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ (видалить дані)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "Позначити/знÑти позначку розділу Ñк ÑтиÑненого (лише btrfs)"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "Видалити підтом"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "Видалити розділ"
+
+#, fuzzy
+msgid "Partition"
+msgstr "Розділи"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * Точки Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñ–Ð² відноÑÑÑ‚ÑŒÑÑ Ð´Ð¾ внутрішньої інÑталÑції, наприклад, завантажувач буде /boot."
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "ÐедійÑний каталог: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "Введіть початковий Ñектор (відÑоток або номер блоку, за замовчуваннÑм: {}): "
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "Введіть кінцевий Ñектор розділу (відÑоток або номер блоку, наприклад: {}): "
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
msgid "Encryption type"
msgstr "Тип шифруваннÑ"
@@ -855,23 +974,323 @@ msgstr "Оберіть параметр ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¸Ñка"
msgid "Select a FIDO2 device to use for HSM"
msgstr "Оберіть приÑтрій FIDO2 Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ HSM"
-msgid "All settings will be reset, are you sure?"
-msgstr "УÑÑ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ Ñкинуто, ви впевнені?"
+#, fuzzy
+msgid "Use a best-effort default partition layout"
+msgstr "Стерти уÑÑ– вибрані диÑки та викориÑтовувати отпимальну Ñхему розмітки розділів за замовчуваннÑм"
-msgid "Back"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "Розділи"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´ÑутнÑ"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr "Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñƒ"
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+#, fuzzy
+msgid "↠Back"
msgstr "Ðазад"
msgid "Disk encryption"
msgstr "Disk encryption"
+#, fuzzy
+msgid "Configuration"
+msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´ÑутнÑ"
+
msgid "Password"
msgstr "Пароль"
-msgid "Partition encryption"
-msgstr "Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñƒ"
+msgid "All settings will be reset, are you sure?"
+msgstr "УÑÑ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ Ñкинуто, ви впевнені?"
+
+msgid "Back"
+msgstr "Ðазад"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "Введіть початковий Ñектор (відÑоток або номер блоку, за замовчуваннÑм: {}): "
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "Введіть кінцевий Ñектор розділу (відÑоток або номер блоку, наприклад: {}): "
+#, fuzzy
+msgid "Installed packages"
+msgstr "Додаткові пакунки"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "Профіль"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "Профіль"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "Видалити інтерфейÑ"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "Профіль"
+
+#, fuzzy
+msgid "The profile name you entered is already in use. Try again"
+msgstr "Введене вами ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача недійÑне. Спробуйте знову"
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "Ðапишіть додаткові пакети Ð´Ð»Ñ Ñ–Ð½ÑталÑції (розділені пробілами, залиште порожнім, щоб пропуÑтити): "
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "Ðапишіть додаткові пакети Ð´Ð»Ñ Ñ–Ð½ÑталÑції (розділені пробілами, залиште порожнім, щоб пропуÑтити): "
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"Оберіть графічний драйвер або залиште поле пуÑтим, щоб уÑтановити вÑÑ– драйвери з відкритим вихідним кодом"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´ÑутнÑ"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "Профіль"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "Оберіть один або кілька жорÑтких диÑків Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð° налаштуваннÑ"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "Додати кориÑтувача"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "Введіть ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача (залиште порожнім, щоб пропуÑтити): "
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "Введіть ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача (залиште порожнім, щоб пропуÑтити): "
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "Оберіть параметр ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¸Ñка"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "Оберіть параметр ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¸Ñка"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "Зберегти конфігурацію кориÑтувача"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "Введіть каталог Ð´Ð»Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ð¹, Ñкі потрібно зберегти: "
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "Зберегти конфігурацію"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "Регіон дзеркала"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "Регіон дзеркала"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - МакÑимальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ : {} ( ДозволÑÑ” {} паралельних завантажень, дозволÑÑ” {} завантажень за раз )"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "Ðекоректне введеннÑ! Повторіть Ñпробу з валідним введеннÑм [від 1 до {} або 0 Ð´Ð»Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ]"
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "ВикориÑтовувати NetworkManager (необхідний Ð´Ð»Ñ Ð³Ñ€Ð°Ñ„Ñ–Ñ‡Ð½Ð¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð†Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚Ñƒ в GNOME та KDE)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "Введіть початковий Ñектор (відÑоток або номер блоку, за замовчуваннÑм: {}): "
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "Введіть початковий Ñектор (відÑоток або номер блоку, за замовчуваннÑм: {}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "Ручне налаштуваннÑ"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "Цей параметр вмикає кількіÑÑ‚ÑŒ паралельних завантажень, Ñкі можуть відбуватиÑÑ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ Ð²ÑтановленнÑ"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"Введіть кількіÑÑ‚ÑŒ паралельних завантажень, Ñкі потрібно ввімкнути.\n"
+" (Введіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ 1 до {})\n"
+"Примітка:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - МакÑимальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ : {} ( ДозволÑÑ” {} паралельних завантажень, дозволÑÑ” {} завантажень за раз )"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - Вимкнути/Типово : 0 ( Вимикає паралельне завантаженнÑ, дозволÑÑ” лише 1 Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð° раз )"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "Ðекоректне введеннÑ! Повторіть Ñпробу з валідним введеннÑм [від 1 до {} або 0 Ð´Ð»Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "Ви бажаєте викориÑтовувати підкачку на zram?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "Видалити інтерфейÑ"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "Позначити/знÑти позначку розділу Ñк ÑтиÑненого (лише btrfs)"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "Бажаєте викориÑтовувати ÑтиÑÐ½ÐµÐ½Ð½Ñ BTRFS?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/ur/LC_MESSAGES/base.mo b/archinstall/locales/ur/LC_MESSAGES/base.mo
index e788d932..887c5126 100644
--- a/archinstall/locales/ur/LC_MESSAGES/base.mo
+++ b/archinstall/locales/ur/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po
index 4e3d7a10..8d2d18c0 100644
--- a/archinstall/locales/ur/LC_MESSAGES/base.po
+++ b/archinstall/locales/ur/LC_MESSAGES/base.po
@@ -62,7 +62,7 @@ msgstr "انسٹال کرنےکے لیے اضاÙÛŒ پیکجز لکھیں (الÚ
msgid "Copy ISO network configuration to installation"
msgstr "آئی ایس او نیٹ ورک Ú©Ù†Ùیگریشن Ú©Ùˆ انسٹالیشن میں کاپی کریں"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "نیٹ ورک مینجر کا استعمال کریں (GNOME اور KDE میں انٹرنیٹ Ú©Ùˆ گراÙیکلی ترتیب دینے Ú©Û’ لیے ضروری ÛÛ’)"
msgid "Select one network interface to configure"
@@ -825,14 +825,13 @@ msgstr ""
msgid "This option enables the number of parallel downloads that can occur during installation"
msgstr ""
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
msgstr ""
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
@@ -867,6 +866,122 @@ msgstr ""
msgid "The font should be stored as {}"
msgstr ""
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr ""
+
+#, fuzzy
+msgid "Select an execution mode"
+msgstr "'{}' کے لیے ایک عمل منتخب کریں"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr ""
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr ""
+
+#, fuzzy
+msgid "Select one or more devices to use and configure"
+msgstr "استعمال کرنے اور Ú©Ù†Ùیگر Ú©Û’ لیے ایک یا Ø²ÛŒØ§Ø¯Û Ûارڈ ڈرائیوز منتخب کریں"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr ""
+
+#, fuzzy
+msgid "Existing Partitions"
+msgstr "پارٹیشن شامل ÛÙˆ رÛÛŒ ÛÛ’..."
+
+#, fuzzy
+msgid "Select a partitioning option"
+msgstr "ایک پارٹیشن کو حذ٠کریں"
+
+#, fuzzy
+msgid "Enter the root directory of the mounted devices: "
+msgstr "محÙوظ کیے جانے والے Ú©Ù†Ùیگریشن Ú©Û’ لیے ایک ڈائرکٹری درج کریں:"
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr ""
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr ""
+
+#, fuzzy
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "ÛŒÛ Ù¾ÛÙ„Û’ سے پروگرام Ø´Ø¯Û Ù¾Ø±ÙˆÙائلز Ú©ÛŒ ÙÛرست ÛÛ’ØŒ ÙˆÛ ÚˆÛŒØ³Ú© ٹاپ انسٹالیشن جیسی چیزوں Ú©Ùˆ آسان بناتے Ûیں"
+
+#, fuzzy
+msgid "Current profile selection"
+msgstr "Ù…ÙˆØ¬ÙˆØ¯Û Ù¾Ø§Ø±Ù¹ÛŒØ´Ù† Ú©ÛŒ ترتیب"
+
+#, fuzzy
+msgid "Remove all newly added partitions"
+msgstr "ایک نیا پارٹیشن بنائیں"
+
+#, fuzzy
+msgid "Assign mountpoint"
+msgstr "پارٹیشن Ú©Û’ لیے ماؤنٹ پوائنٹ تÙویض کریں"
+
+#, fuzzy
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "Ùارمیٹ کرنے Ú©Û’ لیے پارٹیشن Ú©Ùˆ مارک​​ /انمارک کریں (ڈیٹا صا٠کرتا ÛÛ’)"
+
+msgid "Mark/Unmark as bootable"
+msgstr ""
+
+msgid "Change filesystem"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as compressed"
+msgstr "انکرپٹڈ کرنے کے لیے پارٹیشن کو مارک​​ /انمارک کریں"
+
+#, fuzzy
+msgid "Set subvolumes"
+msgstr "صار٠کو حذ٠کریں"
+
+#, fuzzy
+msgid "Delete partition"
+msgstr "ایک پارٹیشن کو حذ٠کریں"
+
+msgid "Partition"
+msgstr ""
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr ""
+
+#, fuzzy
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "* پارٹیشن ماؤنٹ پوائنٹس انسٹالیشن Ú©ÛŒ نسبت سے Ûیں، بوٹ مثال Ú©Û’ طور /boot پرÛوگا۔"
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr ""
+
+msgid "Mountpoint: "
+msgstr ""
+
+msgid "Current free sectors on device {}:"
+msgstr ""
+
+#, fuzzy
+msgid "Total sectors: {}"
+msgstr "درست ڈائریکٹری Ù†Ûیں ÛÛ’: {}"
+
+#, fuzzy
+msgid "Enter the start sector (default: {}): "
+msgstr "اسٹارٹ سیکٹر درج کریں (Ùیصد یا بلاک نمبر، ÚˆÛŒÙالٹ: {}):"
+
+#, fuzzy
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "پارٹیشن کا آخری سیکٹر درج کریں (Ùیصد یا بلاک نمبر، مثال Ú©Û’ طور پر: {}):"
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr ""
+
+msgid "Partition management: {}"
+msgstr ""
+
+msgid "Total length: {}"
+msgstr ""
+
#, fuzzy
msgid "Encryption type"
msgstr "انکرپشن پاس ورڈ سیٹ کریں"
@@ -889,27 +1004,317 @@ msgid "Select a FIDO2 device to use for HSM"
msgstr ""
#, fuzzy
-msgid "All settings will be reset, are you sure?"
-msgstr "{} پارٹیشنزکے گروپ پر مشتمل ÛÛ’ØŒ ÛŒÛ Ø§Ù† Ú©Ùˆ مٹا دے گا، کیا آپ پر اعتماد Ûیں؟"
+msgid "Use a best-effort default partition layout"
+msgstr "تمام منتخب ڈرائیوز Ú©Ùˆ صا٠کریں اور ایک بÛترین ÚˆÛŒÙالٹ پارٹیشن Ù„Û’ آؤٹ استعمال کریں"
-msgid "Back"
+#, fuzzy
+msgid "Manual Partitioning"
+msgstr "ترتیب Ú©Ùˆ محÙوظ کریں"
+
+#, fuzzy
+msgid "Pre-mounted configuration"
+msgstr "کوئی Ú©Ù†Ùیگریشن Ù†Ûیں"
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Partition encryption"
+msgstr ""
+
+msgid " ! Formatting {} in "
+msgstr ""
+
+msgid "↠Back"
msgstr ""
msgid "Disk encryption"
msgstr ""
#, fuzzy
+msgid "Configuration"
+msgstr "کوئی Ú©Ù†Ùیگریشن Ù†Ûیں"
+
+#, fuzzy
msgid "Password"
msgstr "روٹ پاس ورڈ"
-msgid "Partition encryption"
+#, fuzzy
+msgid "All settings will be reset, are you sure?"
+msgstr "{} پارٹیشنزکے گروپ پر مشتمل ÛÛ’ØŒ ÛŒÛ Ø§Ù† Ú©Ùˆ مٹا دے گا، کیا آپ پر اعتماد Ûیں؟"
+
+msgid "Back"
+msgstr ""
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr ""
+
+msgid "Environment type: {}"
+msgstr ""
+
+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 ""
+
+#, fuzzy
+msgid "Installed packages"
+msgstr "اضاÙÛŒ پیکجز"
+
+#, fuzzy
+msgid "Add profile"
+msgstr "پروÙائل"
+
+#, fuzzy
+msgid "Edit profile"
+msgstr "پروÙائل"
+
+#, fuzzy
+msgid "Delete profile"
+msgstr "صار٠کو حذ٠کریں"
+
+#, fuzzy
+msgid "Profile name: "
+msgstr "پروÙائل"
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr ""
+
+#, fuzzy
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "انسٹال کرنےکے لیے اضاÙÛŒ پیکجز لکھیں (الگ الگ لیکھیں، Ù†Ûیں Ú©ÛŒ صورت میں خالی چھوڑیں):"
+
+#, fuzzy
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "انسٹال کرنےکے لیے اضاÙÛŒ پیکجز لکھیں (الگ الگ لیکھیں، Ù†Ûیں Ú©ÛŒ صورت میں خالی چھوڑیں):"
+
+msgid "Should this profile be enabled for installation?"
+msgstr ""
+
+msgid "Create your own"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"\n"
+"ایک گراÙکس ڈرائیور منتخب کریں یا تمام اوپن سورس ڈرائیورز Ú©Ùˆ انسٹال کرنے Ú©Û’ لیے خالی Ú†Ú¾ÙˆÚ‘ دیں"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+
+msgid "Graphics driver"
+msgstr ""
+
+msgid "Greeter"
+msgstr ""
+
+msgid "Please chose which greeter to install"
+msgstr ""
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr ""
+
+#, fuzzy
+msgid "Disk configuration"
+msgstr "کوئی Ú©Ù†Ùیگریشن Ù†Ûیں"
+
+#, fuzzy
+msgid "Profiles"
+msgstr "پروÙائل"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr ""
+
+#, fuzzy
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "استعمال کرنے اور Ú©Ù†Ùیگر Ú©Û’ لیے ایک یا Ø²ÛŒØ§Ø¯Û Ûارڈ ڈرائیوز منتخب کریں"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "ایک صار٠شامل کریں"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "ایک اضاÙÛŒ صار٠بنانے Ú©Û’ لیے صار٠نام درج کریں (Ú†Ú¾ÙˆÚ‘Ù†Û’ Ú©Û’ لیے خالی چھوڑیں):"
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "ایک اضاÙÛŒ صار٠بنانے Ú©Û’ لیے صار٠نام درج کریں (Ú†Ú¾ÙˆÚ‘Ù†Û’ Ú©Û’ لیے خالی چھوڑیں):"
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "ڈسک لے آؤٹ کو منتخب کریں"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "ڈسک لے آؤٹ کو منتخب کریں"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "صار٠کنÙیگریشن Ú©Ùˆ محÙوظ کریں"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "محÙوظ کیے جانے والے Ú©Ù†Ùیگریشن Ú©Û’ لیے ایک ڈائرکٹری درج کریں:"
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "اسٹارٹ سیکٹر درج کریں (Ùیصد یا بلاک نمبر، ÚˆÛŒÙالٹ: {}):"
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "ترتیب Ú©Ùˆ محÙوظ کریں"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "متبادل علاقÛ"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "متبادل علاقÛ"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr ""
+
+msgid "Locales"
+msgstr ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "پارٹیشن کا آخری سیکٹر درج کریں (Ùیصد یا بلاک نمبر، مثال Ú©Û’ طور پر: {}):"
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "نیٹ ورک مینجر کا استعمال کریں (GNOME اور KDE میں انٹرنیٹ Ú©Ùˆ گراÙیکلی ترتیب دینے Ú©Û’ لیے ضروری ÛÛ’)"
+
+msgid "Total: {} / {}"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "اسٹارٹ سیکٹر درج کریں (Ùیصد یا بلاک نمبر، ÚˆÛŒÙالٹ: {}):"
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "اسٹارٹ سیکٹر درج کریں (Ùیصد یا بلاک نمبر، ÚˆÛŒÙالٹ: {}):"
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "ترتیب Ú©Ùˆ محÙوظ کریں"
+
+msgid "Type"
+msgstr ""
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr ""
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr ""
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr ""
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr ""
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr ""
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "کیا آپ زی ریم پر سواپ استعمال کرنا چاÛیں Ú¯Û’ØŸ"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "صار٠کو حذ٠کریں"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "انکرپٹڈ کرنے کے لیے پارٹیشن کو مارک​​ /انمارک کریں"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "کیا آپ زی ریم پر سواپ استعمال کرنا چاÛیں Ú¯Û’ØŸ"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
#~ msgid "Add :"
#~ msgstr "شامل:"
diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo
index 693308ab..b7d388bf 100644
--- a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo
+++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po
index d957204b..7f008e52 100644
--- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po
+++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po
@@ -3,40 +3,40 @@ msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
-"Last-Translator: Xiaotian Wu <yetist@gmail.com>\n"
+"Last-Translator: clsty <celestial.y@outlook.com>\n"
"Language-Team: \n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.1.1\n"
+"X-Generator: Poedit 3.3.2\n"
msgid "[!] A log file has been created here: {} {}"
-msgstr "[!] 已在此处创建日志文件:{} {}"
+msgstr "[!] 一份日志文件已在此处创建:{} {}"
msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
msgstr " 请将此问题(和文件)æ交到 https://github.com/archlinux/archinstall/issues"
msgid "Do you really want to abort?"
-msgstr "你真的想中止å—?"
+msgstr "您真的è¦ä¸­æ­¢å—?"
msgid "And one more time for verification: "
-msgstr "还有一次验è¯ï¼š "
+msgstr "å†è¾“入一次以进行验è¯ï¼š"
msgid "Would you like to use swap on zram?"
-msgstr "你想在 zram 上使用交æ¢å—?"
+msgstr "您想在 zram 上使用交æ¢åˆ†åŒºï¼ˆswap)å—?"
msgid "Desired hostname for the installation: "
-msgstr "安装所需的主机å: "
+msgstr "请输入安装åŽæœŸæœ›ä½¿ç”¨çš„主机å(hostname):"
msgid "Username for required superuser with sudo privileges: "
-msgstr "具有 sudo æƒé™çš„所需超级用户的用户å: "
+msgstr "请输入需è¦è¶…级用户的用户å(sudo æƒé™ï¼‰ï¼š"
msgid "Any additional users to install (leave blank for no users): "
-msgstr "è¦æ–°å»ºçš„任何其他用户(留空表示没有用户): "
+msgstr "请输入è¦å®‰è£…的其他用户(留空表示ä¸å®‰è£…其他用户):"
msgid "Should this user be a superuser (sudoer)?"
-msgstr "这个用户应该是超级用户(sudoer)å—?"
+msgstr "这个用户应该æˆä¸ºè¶…级用户(sudoer)å—?"
msgid "Select a timezone"
msgstr "选择时区"
@@ -48,62 +48,62 @@ msgid "Choose a bootloader"
msgstr "选择引导加载程åº"
msgid "Choose an audio server"
-msgstr "选择音频æœåŠ¡å™¨"
+msgstr "请选择一个音频æœåŠ¡å™¨ï¼ˆaudio server)"
msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
-msgstr "仅安装 baseã€base-develã€linuxã€linux-firmwareã€efibootmgr å’Œå¯â€‹â€‹é€‰é…置文件包等软件包。"
+msgstr "仅安装基本软件包(base)ã€åŸºæœ¬å¼€å‘软件包(base-devel)ã€Linux 内核(linux)ã€Linux 固件(linux-firmware)ã€efibootmgr å’Œå¯é€‰çš„é…置文件软件包(profile packages)。"
msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
-msgstr "如果你想è¦ä¸€ä¸ª web æµè§ˆå™¨ï¼Œä¾‹å¦‚ firefox 或 chromium,你å¯ä»¥åœ¨ä¸‹é¢çš„æ示中指定它。"
+msgstr "如果您需è¦ä¸€ä¸ªç½‘络æµè§ˆå™¨ï¼Œä¾‹å¦‚ firefox 或 chromium,您å¯ä»¥åœ¨ä¸‹é¢çš„æ示中指定它。"
msgid "Write additional packages to install (space separated, leave blank to skip): "
-msgstr "编写è¦å®‰è£…的附加软件包(空格分隔,留空跳过): "
+msgstr "输入è¦å®‰è£…的其他软件包(空格分隔,留空以跳过):"
msgid "Copy ISO network configuration to installation"
-msgstr "å°† ISO 网络é…ç½®å¤åˆ¶åˆ°å®‰è£…"
+msgstr "å°† ISO 中的网络é…ç½®å¤åˆ¶åˆ°å®‰è£…目标中"
-msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
msgstr "使用 NetworkManager(在 GNOME å’Œ KDE 中以图形方å¼é…ç½® Internet 所必需的)"
msgid "Select one network interface to configure"
-msgstr "选择一个网络接å£è¿›è¡Œé…ç½®"
+msgstr "选择è¦é…置的网络接å£"
msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
msgstr "选择è¦ä¸ºâ€œ{}â€é…置的模å¼æˆ–跳过以使用默认模å¼â€œ{}â€"
msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
-msgstr "输入 {} çš„ IP å’Œå­ç½‘(例如:192.168.0.5/24): "
+msgstr "输入 {} çš„ IP å’Œå­ç½‘(例如:192.168.0.5/24):"
msgid "Enter your gateway (router) IP address or leave blank for none: "
-msgstr "输入您的网关(路由器)IP 地å€æˆ–留空为无: "
+msgstr "请输入您的网关(路由器)IP 地å€ï¼Œå¦‚果没有请留空:"
msgid "Enter your DNS servers (space separated, blank for none): "
-msgstr "输入您的 DNS æœåŠ¡å™¨ï¼ˆç©ºæ ¼åˆ†éš”,空白表示无): "
+msgstr "请输入您的 DNS æœåŠ¡å™¨åœ°å€ï¼ˆä»¥ç©ºæ ¼åˆ†éš”,如果没有请留空):"
msgid "Select which filesystem your main partition should use"
-msgstr "选择你的主分区应该使用哪个文件系统"
+msgstr "选择您的主分区应使用的文件系统"
msgid "Current partition layout"
-msgstr "当å‰åˆ†åŒºå¸ƒå±€"
+msgstr "当å‰çš„分区布局"
msgid ""
"Select what to do with\n"
"{}"
msgstr ""
-"选择è¦åšä»€ä¹ˆ\n"
+"选择è¦æ‰§è¡Œçš„æ“作\n"
"{}"
msgid "Enter a desired filesystem type for the partition"
msgstr "为分区输入所需的文件系统类型"
msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
-msgstr ""
+msgstr "输入起始ä½ç½®ï¼ˆå•ä½ï¼šs,GB,% 等;默认值:{}):"
msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
-msgstr ""
+msgstr "输入结æŸä½ç½®ï¼ˆå•ä½ï¼šs,GB,% 等;例如:{}):"
msgid "{} contains queued partitions, this will remove those, are you sure?"
-msgstr "{} 包å«æŽ’队分区,这将删除这些分区,您确定å—?"
+msgstr "{} 包å«äº†å·²æŽ’队的分区,这将会删除这些分区,您确定å—?"
msgid ""
"{}\n"
@@ -121,13 +121,13 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"按索引选择è¦æŒ‚载的分区"
+"按索引选择è¦æŒ‚载的分区åŠæŒ‚è½½ä½ç½®"
msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
-msgstr " * 分区挂载点是相对于安装内部的,例如 boot 应该为 /boot。"
+msgstr " * 分区挂载点是相对于安装目标的目录内部的,例如引导分区(boot)的挂载点为 /boot。"
msgid "Select where to mount partition (leave blank to remove mountpoint): "
-msgstr "选择挂载分区的ä½ç½®ï¼ˆç•™ç©ºä»¥åˆ é™¤æŒ‚载点): "
+msgstr "选择è¦æŒ‚载分区的ä½ç½®ï¼ˆç•™ç©ºè¡¨ç¤ºç§»é™¤æŒ‚载点):"
msgid ""
"{}\n"
@@ -136,7 +136,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"选择è¦æ ‡è®°çš„分区以进行格å¼åŒ–"
+"选择è¦æ ¼å¼åŒ–的分区"
msgid ""
"{}\n"
@@ -145,7 +145,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"选择è¦æ ‡è®°ä¸ºåŠ å¯†çš„分区"
+"选择è¦åŠ å¯†çš„分区"
msgid ""
"{}\n"
@@ -163,40 +163,40 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"选择è¦åœ¨å“ªä¸ªåˆ†åŒºä¸Šè®¾ç½®æ–‡ä»¶ç³»ç»Ÿ"
+"选择è¦è®¾ç½®æ–‡ä»¶ç³»ç»Ÿçš„分区"
msgid "Enter a desired filesystem type for the partition: "
-msgstr "为分区输入所需的文件系统类型: "
+msgstr "为分区选择所需的文件系统类型:"
msgid "Archinstall language"
-msgstr "Archinstall ç•Œé¢è¯­è¨€"
+msgstr "Archinstall 语言"
msgid "Wipe all selected drives and use a best-effort default partition layout"
-msgstr "擦除所有选定的驱动器并使用最优的默认分区布局"
+msgstr "擦除所有选定的驱动器并使用最佳的默认分区布局"
msgid "Select what to do with each individual drive (followed by partition usage)"
-msgstr "选择如何处ç†æ¯ä¸ªå•ç‹¬çš„驱动器(其次是分区使用情况)"
+msgstr "选择对æ¯ä¸ªå•ç‹¬çš„驱动器执行的æ“作(åŽè·Ÿåˆ†åŒºä½¿ç”¨æƒ…况)"
msgid "Select what you wish to do with the selected block devices"
-msgstr "选择您希望对所选å—设备执行的æ“作"
+msgstr "选择è¦å¯¹æ‰€é€‰çš„(一个或多个)å—设备(block device)执行的æ“作"
msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
-msgstr "这是预编程é…置文件的列表,它们å¯èƒ½ä½¿å®‰è£…æ¡Œé¢çŽ¯å¢ƒä¹‹ç±»çš„东西å˜å¾—更容易"
+msgstr "这份列表列出了预先编写的é…置文件,它们å¯èƒ½ä¼šä½¿å®‰è£…æ¡Œé¢çŽ¯å¢ƒç­‰å†…容å˜å¾—更加容易"
msgid "Select keyboard layout"
msgstr "选择键盘布局"
msgid "Select one of the regions to download packages from"
-msgstr "选择è¦ä»Žä¸­ä¸‹è½½è½¯ä»¶åŒ…的区域之一"
+msgstr "选择一个è¦ä»Žä¸­ä¸‹è½½è½¯ä»¶åŒ…的区域"
msgid "Select one or more hard drives to use and configure"
-msgstr "选择一个或多个硬盘驱动器æ¥ä½¿ç”¨å’Œé…ç½®"
+msgstr "选择è¦ä½¿ç”¨å’Œé…置的硬盘驱动器(å¯å¤šé€‰ï¼‰"
msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
-msgstr "为了与您的 AMD 硬件实现最佳兼容性,您å¯èƒ½éœ€è¦ä½¿ç”¨æ‰€æœ‰å¼€æºæˆ– AMD / ATI 选项。"
+msgstr "为了与您的 AMD 硬件实现最佳兼容性,您å¯èƒ½éœ€è¦ä½¿ç”¨æ‰€æœ‰å¼€æºæ˜¾å¡é©±åŠ¨ç¨‹åºæˆ– 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 "为了与您的 Intel 硬件实现最佳兼容性,您å¯èƒ½éœ€è¦ä½¿ç”¨æ‰€æœ‰å¼€æºæˆ– Intel 选项。\n"
+msgstr "为了与您的 Intel 硬件实现最佳兼容性,您å¯èƒ½éœ€è¦ä½¿ç”¨æ‰€æœ‰å¼€æºæ˜¾å¡é©±åŠ¨ç¨‹åºæˆ– Intel 选项。\n"
msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
msgstr "为了与您的 Nvidia 硬件实现最佳兼容性,您å¯èƒ½éœ€è¦ä½¿ç”¨ Nvidia 专有驱动程åºã€‚\n"
@@ -208,13 +208,13 @@ msgid ""
msgstr ""
"\n"
"\n"
-"选择图形驱动程åºæˆ–留空以安装所有开æºé©±åŠ¨ç¨‹åº"
+"选择一个显å¡é©±åŠ¨ç¨‹åºï¼Œæˆ–留空以安装所有开æºé©±åŠ¨ç¨‹åº"
msgid "All open-source (default)"
-msgstr "全部开æºï¼ˆé»˜è®¤ï¼‰"
+msgstr "所有开æºæ˜¾å¡é©±åŠ¨ç¨‹åºï¼ˆé»˜è®¤ï¼‰"
msgid "Choose which kernels to use or leave blank for default \"{}\""
-msgstr "选择è¦ä½¿ç”¨çš„内核或将默认“{}â€ç•™ç©º"
+msgstr "选择è¦ä½¿ç”¨çš„内核或留空以使用默认值“{}â€"
msgid "Choose which locale language to use"
msgstr "选择è¦ä½¿ç”¨çš„语言环境"
@@ -223,19 +223,19 @@ msgid "Choose which locale encoding to use"
msgstr "选择è¦ä½¿ç”¨çš„语言环境编ç "
msgid "Select one of the values shown below: "
-msgstr "选择如下所示的值之一: "
+msgstr "选择以下值之一:"
msgid "Select one or more of the options below: "
-msgstr "选择以下一个或多个选项: "
+msgstr "选择以下选项之一或多个选项:"
msgid "Adding partition...."
-msgstr "添加分区...."
+msgstr "正在添加分区……"
msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
-msgstr "您需è¦è¾“入有效的文件系统类型æ‰èƒ½ç»§ç»­ã€‚有关有效的文件系统类型,请å‚阅 `man parted`。"
+msgstr "您需è¦è¾“入有效的文件系统类型æ‰èƒ½ç»§ç»­ã€‚请å‚阅 `man parted` 以获å–有效的文件系统类型。"
msgid "Error: Listing profiles on URL \"{}\" resulted in:"
-msgstr "错误:在 URL“{}â€ä¸Šåˆ—出é…置文件导致:"
+msgstr "错误:列出ä½äºŽ URL “{}†上的é…置文件时出错:"
msgid "Error: Could not decode \"{}\" result as JSON:"
msgstr "错误:无法将“{}â€ç»“果解ç ä¸º JSON:"
@@ -247,10 +247,10 @@ msgid "Mirror region"
msgstr "é•œåƒåŒºåŸŸ"
msgid "Locale language"
-msgstr "本地语言"
+msgstr "语言环境"
msgid "Locale encoding"
-msgstr "本地编ç "
+msgstr "语言环境编ç "
msgid "Drive(s)"
msgstr "驱动器"
@@ -286,16 +286,16 @@ msgid "Kernels"
msgstr "内核"
msgid "Additional packages"
-msgstr "附加包"
+msgstr "附加软件包"
msgid "Network configuration"
msgstr "网络é…ç½®"
msgid "Automatic time sync (NTP)"
-msgstr "自动时间åŒæ­¥ (NTP)"
+msgstr "自动时间åŒæ­¥ï¼ˆNTP)"
msgid "Install ({} config(s) missing)"
-msgstr "安装(缺少 {} 个é…置)"
+msgstr "安装({} 个é…置缺失)"
msgid ""
"You decided to skip harddrive selection\n"
@@ -303,16 +303,16 @@ msgid ""
"WARNING: Archinstall won't check the suitability of this setup\n"
"Do you wish to continue?"
msgstr ""
-"你决定跳过硬盘选择\n"
-"并将使用安装在 {} 上的任何驱动器设置(实验性)\n"
-"警告:Archinstall ä¸ä¼šæ£€æŸ¥æ­¤è®¾ç½®çš„适用性\n"
-"你想继续å—?"
+"您决定跳过硬盘驱动器选择\n"
+"并将使用挂载在 {} 上的任何驱动器设置(实验性)\n"
+"警告:Archinstall å°†ä¸ä¼šæ£€æŸ¥æ­¤è®¾ç½®çš„适用性\n"
+"您是å¦è¦ç»§ç»­ï¼Ÿ"
msgid "Re-using partition instance: {}"
-msgstr "é‡ç”¨åˆ†åŒºå®žä¾‹ï¼š{}"
+msgstr "正在é‡æ–°ä½¿ç”¨åˆ†åŒºå®žä¾‹ï¼š{}"
msgid "Create a new partition"
-msgstr "创建新分区"
+msgstr "创建一个新分区"
msgid "Delete a partition"
msgstr "删除一个分区"
@@ -324,13 +324,13 @@ msgid "Assign mount-point for a partition"
msgstr "为分区分é…挂载点"
msgid "Mark/Unmark a partition to be formatted (wipes data)"
-msgstr "标记/å–消标记è¦æ ¼å¼åŒ–的分区(擦除数æ®ï¼‰"
+msgstr "将分区标记/å–消标记为格å¼åŒ–(擦除数æ®ï¼‰"
msgid "Mark/Unmark a partition as encrypted"
msgstr "将分区标记/å–消标记为加密"
msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
-msgstr "将分区标记/å–消标记为å¯å¼•å¯¼ï¼ˆè‡ªåŠ¨ä¸º /boot)"
+msgstr "将分区标记/å–消标记为å¯å¼•å¯¼ï¼ˆ/boot 会自动设置为å¯å¼•å¯¼ï¼‰"
msgid "Set desired filesystem for a partition"
msgstr "为分区设置所需的文件系统"
@@ -342,13 +342,13 @@ msgid "Hostname"
msgstr "主机å"
msgid "Not configured, unavailable unless setup manually"
-msgstr "未é…置,除éžæ‰‹åŠ¨è®¾ç½®ï¼Œå¦åˆ™ä¸å¯ç”¨"
+msgstr "未é…置;除éžæ‰‹åŠ¨è®¾ç½®ï¼Œå¦åˆ™ä¸å¯ç”¨"
msgid "Timezone"
msgstr "时区"
msgid "Set/Modify the below options"
-msgstr "请设置/修改以下选项"
+msgstr "设置/修改以下选项"
msgid "Install"
msgstr "安装"
@@ -357,55 +357,55 @@ msgid ""
"Use ESC to skip\n"
"\n"
msgstr ""
-"按 ESC 键跳过\n"
+"按 ESC 跳过\n"
"\n"
msgid "Suggest partition layout"
msgstr "建议分区布局"
msgid "Enter a password: "
-msgstr "输入密ç ï¼š "
+msgstr "输入密ç ï¼š"
msgid "Enter a encryption password for {}"
-msgstr "输入 {} 的加密密ç "
+msgstr "为 {} 输入一个加密密ç "
msgid "Enter disk encryption password (leave blank for no encryption): "
-msgstr "输入ç£ç›˜åŠ å¯†å¯†ç ï¼ˆç•™ç©ºè¡¨ç¤ºä¸åŠ å¯†ï¼‰ï¼š "
+msgstr "输入ç£ç›˜åŠ å¯†å¯†ç ï¼ˆç•™ç©ºåˆ™ä¸åŠ å¯†ï¼‰ï¼š"
msgid "Create a required super-user with sudo privileges: "
-msgstr "创建具有 sudo æƒé™çš„必需超级用户: "
+msgstr "创建所需的具有 sudo æƒé™çš„超级用户:"
msgid "Enter root password (leave blank to disable root): "
-msgstr "输入 root 密ç ï¼ˆç•™ç©ºä»¥ç¦ç”¨ root): "
+msgstr "输入 root 密ç ï¼ˆç•™ç©ºä»¥ç¦ç”¨ root):"
msgid "Password for user \"{}\": "
-msgstr "用户“{}â€çš„密ç ï¼š "
+msgstr "用户“{}â€çš„密ç ï¼š"
msgid "Verifying that additional packages exist (this might take a few seconds)"
-msgstr "验è¯æ˜¯å¦å­˜åœ¨å…¶ä»–软件包(这å¯èƒ½éœ€è¦å‡ ç§’钟)"
+msgstr "正在验è¯é™„加软件包是å¦å­˜åœ¨ï¼ˆè¿™å¯èƒ½éœ€è¦å‡ ç§’或几å秒)"
msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
-msgstr "是å¦è¦å¯¹é»˜è®¤æ—¶é—´æœåŠ¡å™¨ä½¿ç”¨è‡ªåŠ¨æ—¶é—´åŒæ­¥ (NTP)?\n"
+msgstr "您是å¦å¸Œæœ›ä½¿ç”¨é»˜è®¤æ—¶é—´æœåŠ¡å™¨è¿›è¡Œè‡ªåŠ¨æ—¶é—´åŒæ­¥ï¼ˆNTP)?\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 ""
-"为了使 NTP 工作,å¯èƒ½éœ€è¦ç¡¬ä»¶æ—¶é—´å’Œå…¶ä»–é…ç½®åŽæ­¥éª¤ã€‚\n"
-"更多信æ¯ï¼Œè¯·æŸ¥çœ‹ Arch wiki"
+"为了使 NTP 正常工作,å¯èƒ½éœ€è¦åœ¨ä¹‹åŽè¿›è¡Œç¡¬ä»¶æ—¶é—´åŠå…¶ä»–é…置的步骤。\n"
+"更多信æ¯ï¼Œè¯·æŸ¥é˜… Arch wiki"
msgid "Enter a username to create an additional user (leave blank to skip): "
-msgstr "输入用户å以创建其他用户(留空以跳过): "
+msgstr "输入用户å以创建其他用户(留空以跳过):"
msgid "Use ESC to skip\n"
-msgstr "按 ESC 键跳过\n"
+msgstr "按 ESC 跳过\n"
msgid ""
"\n"
" Choose an object from the list, and select one of the available actions for it to execute"
msgstr ""
"\n"
-"从列表中选择一个对象,然åŽä¸ºå…¶é€‰æ‹©ä¸€ä¸ªå¯ç”¨çš„æ“作"
+" 从列表中选å–一个对象,并选择一个è¦æ‰§è¡Œçš„å¯é€‰æ“作"
msgid "Cancel"
msgstr "å–消"
@@ -432,7 +432,7 @@ msgid "Copy to new key:"
msgstr "å¤åˆ¶åˆ°æ–°å¯†é’¥ï¼š"
msgid "Unknown nic type: {}. Possible values are {}"
-msgstr "未知网å¡ç±»åž‹ï¼š{}。 å¯èƒ½çš„值为 {}"
+msgstr "未知的网å¡ç±»åž‹ï¼š{}。å¯èƒ½çš„值为 {}"
msgid ""
"\n"
@@ -442,10 +442,10 @@ msgstr ""
"这是您选择的é…置:"
msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
-msgstr "Pacman å·²ç»åœ¨è¿è¡Œï¼Œæœ€å¤šç­‰å¾… 10 分钟以使其终止。"
+msgstr "Pacman å·²ç»åœ¨è¿è¡Œï¼Œæœ€å¤šç­‰å¾… 10 分钟或直到它终止。"
msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
-msgstr "预先存在的 pacman é”从未退出。 请在使用 archinstall 之å‰æ¸…ç†æ‰€æœ‰çŽ°æœ‰çš„ pacman 会è¯ã€‚"
+msgstr "预先存在的 pacman é”从未退出。请在使用 archinstall 之å‰æ¸…ç†ä»»ä½•çŽ°æœ‰çš„ pacman 会è¯ã€‚"
msgid "Choose which optional additional repositories to enable"
msgstr "选择è¦å¯ç”¨çš„å¯é€‰é™„加仓库"
@@ -467,16 +467,16 @@ msgid ""
"Define a new user\n"
msgstr ""
"\n"
-"定义新用户\n"
+"定义一个新用户\n"
msgid "User Name : "
-msgstr "用户å: "
+msgstr "用户å:"
msgid "Should {} be a superuser (sudoer)?"
-msgstr "{} 应该是超级用户 (sudoer) å—?"
+msgstr "是å¦å°† {} 设置为超级用户(sudoer)?"
msgid "Define users with sudo privilege: "
-msgstr "定义具有 sudo æƒé™çš„用户: "
+msgstr "定义具有 sudo æƒé™çš„用户:"
msgid "No network configuration"
msgstr "无网络é…ç½®"
@@ -491,7 +491,7 @@ msgid ""
msgstr ""
"{}\n"
"\n"
-"选择è¦åœ¨å“ªä¸ªåˆ†åŒºä¸Šè®¾ç½®å­å·"
+"选择è¦è®¾ç½®å­å·çš„分区"
msgid "Manage btrfs subvolumes for current partition"
msgstr "管ç†å½“å‰åˆ†åŒºçš„ btrfs å­å·"
@@ -515,16 +515,16 @@ msgid "Choose which configuration to save"
msgstr "选择è¦ä¿å­˜çš„é…ç½®"
msgid "Enter a directory for the configuration(s) to be saved: "
-msgstr "输入è¦ä¿å­˜é…置的目录: "
+msgstr "输入è¦ä¿å­˜é…置的目录:"
msgid "Not a valid directory: {}"
msgstr "ä¸æ˜¯æœ‰æ•ˆçš„目录:{}"
msgid "The password you are using seems to be weak,"
-msgstr "您使用的密ç ä¼¼ä¹Žå¾ˆå¼±,"
+msgstr "您正在使用的密ç ä¼¼ä¹Žå¾ˆå¼±ï¼Œ"
msgid "are you sure you want to use it?"
-msgstr "你确定è¦ä½¿ç”¨å®ƒå—?"
+msgstr "您确定è¦ä½¿ç”¨å®ƒå—?"
msgid "Optional repositories"
msgstr "å¯é€‰ä»“库"
@@ -533,35 +533,35 @@ msgid "Save configuration"
msgstr "ä¿å­˜é…ç½®"
msgid "Missing configurations:\n"
-msgstr "缺少é…ç½®:\n"
+msgstr "缺少é…置:\n"
msgid "Either root-password or at least 1 superuser must be specified"
msgstr "必须指定 root 密ç æˆ–至少 1 个超级用户"
msgid "Manage superuser accounts: "
-msgstr "管ç†è¶…级用户å¸æˆ·ï¼š "
+msgstr "管ç†è¶…级用户账户:"
msgid "Manage ordinary user accounts: "
-msgstr "管ç†æ™®é€šç”¨æˆ·è´¦æˆ·ï¼š "
+msgstr "管ç†æ™®é€šç”¨æˆ·è´¦æˆ·ï¼š"
msgid " Subvolume :{:16}"
-msgstr " å­å· :{:16}"
+msgstr " å­å·ï¼š{:16}"
msgid " mounted at {:16}"
-msgstr " 安装在 {:16}"
+msgstr " 挂载在 {:16}"
msgid " with option {}"
-msgstr " 带选项 {}"
+msgstr " 与选项 {}"
msgid ""
"\n"
" Fill the desired values for a new subvolume \n"
msgstr ""
"\n"
-" 为新的å­å·å¡«å……所需的值\n"
+" 为新å­å·å¡«å†™æ‰€éœ€çš„值 \n"
msgid "Subvolume name "
-msgstr "å­å·å "
+msgstr "å­å·å称 "
msgid "Subvolume mountpoint"
msgstr "å­å·æŒ‚载点"
@@ -576,19 +576,19 @@ msgid "Subvolume name :"
msgstr "å­å·å称:"
msgid "Select a mount point :"
-msgstr "选择挂载点:"
+msgstr "选择一个挂载点:"
msgid "Select the desired subvolume options "
msgstr "选择所需的å­å·é€‰é¡¹ "
msgid "Define users with sudo privilege, by username: "
-msgstr "通过用户å定义具有 sudo æƒé™çš„用户: "
+msgstr "通过用户å定义具有 sudo æƒé™çš„用户:"
msgid "[!] A log file has been created here: {}"
-msgstr "[!] 已在此处创建日志文件:{}"
+msgstr "[!] 已在此处创建一份日志文件:{}"
msgid "Would you like to use BTRFS subvolumes with a default structure?"
-msgstr "您想使用具有默认结构的 BTRFS å­å·å—?"
+msgstr "您想以默认结构使用 BTRFS å­å·å—?"
msgid "Would you like to use BTRFS compression?"
msgstr "您想使用 BTRFS 压缩å—?"
@@ -597,7 +597,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"
msgid "Minimum capacity for /home partition: {}GB\n"
msgstr "/home 分区的最å°å®¹é‡ï¼š{}GB\n"
@@ -618,13 +618,13 @@ msgid "set: {}"
msgstr "设置:{}"
msgid "Manual configuration setting must be a list"
-msgstr "手动é…置设置必须是列表"
+msgstr "手动é…置的设置必须是一个列表"
msgid "No iface specified for manual configuration"
-msgstr "没有为手动é…置指定 iface"
+msgstr "没有为手动é…置指定网å¡æŽ¥å£"
msgid "Manual nic configuration with no auto DHCP requires an IP address"
-msgstr "没有自动 DHCP 的手动 nic é…ç½®éœ€è¦ IP 地å€"
+msgstr "ä¸å…·å¤‡è‡ªåŠ¨ DHCP 的手动网å¡é…置需è¦ä¸€ä¸ª IP 地å€"
msgid "Add interface"
msgstr "添加接å£"
@@ -645,43 +645,43 @@ msgid "Mark/Unmark a partition as compressed (btrfs only)"
msgstr "将分区标记/å–æ¶ˆæ ‡è®°ä¸ºåŽ‹ç¼©ï¼ˆä»…é™ btrfs)"
msgid "The password you are using seems to be weak, are you sure you want to use it?"
-msgstr "您使用的密ç ä¼¼ä¹Žå¾ˆå¼±ï¼Œæ‚¨ç¡®å®šè¦ä½¿ç”¨å—?"
+msgstr "您正在使用的密ç ä¼¼ä¹Žå¾ˆå¼±ï¼Œæ‚¨ç¡®å®šè¦ä½¿ç”¨å®ƒå—?"
msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
-msgstr "æ供一系列桌é¢çŽ¯å¢ƒå’Œå¹³é“ºçª—å£ç®¡ç†å™¨ï¼Œä¾‹å¦‚ gnome, kde, sway"
+msgstr "æ供一系列桌é¢çŽ¯å¢ƒå’Œå¹³é“ºçª—å£ç®¡ç†å™¨ä¾›é€‰æ‹©ï¼Œä¾‹å¦‚ gnomeã€kdeã€sway"
msgid "Select your desired desktop environment"
-msgstr "选择您想è¦çš„æ¡Œé¢çŽ¯å¢ƒ"
+msgstr "选择您所需的桌é¢çŽ¯å¢ƒ"
msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
msgstr "一个éžå¸¸åŸºæœ¬çš„安装,å…许您根æ®éœ€è¦è‡ªå®šä¹‰ Arch Linux。"
msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
-msgstr "æ供一系列å¯ä¾›å®‰è£…å’Œå¯ç”¨çš„æœåŠ¡å™¨åŒ…,例如 httpdã€nginxã€mariadb"
+msgstr "æ供一系列的多ç§æœåŠ¡å™¨è½¯ä»¶åŒ…以供安装å¯ç”¨ï¼Œä¾‹å¦‚ httpdã€nginxã€mariadb"
msgid "Choose which servers to install, if none then a minimal installation will be done"
-msgstr "选择è¦å®‰è£…çš„æœåŠ¡å™¨ï¼Œå¦‚果没有,则进行最å°å®‰è£…"
+msgstr "选择è¦å®‰è£…çš„æœåŠ¡å™¨ï¼Œè‹¥æ— åˆ™å°†æ‰§è¡Œæœ€å°åŒ–安装"
msgid "Installs a minimal system as well as xorg and graphics drivers."
-msgstr "安装最å°ç³»ç»Ÿä»¥åŠ xorg 和图形驱动程åºã€‚"
+msgstr "安装一个最å°åŒ–ç³»ç»Ÿä»¥åŠ xorg 和显å¡é©±åŠ¨ç¨‹åºã€‚"
msgid "Press Enter to continue."
-msgstr "按回车键继续。"
+msgstr "按 Enter 继续。"
msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
-msgstr "您想 chroot 进入新创建的安装并执行安装åŽé…ç½®å—?"
+msgstr "您是å¦æƒ³è¦ chroot 到新创建的系统内以进行安装åŽçš„é…置?"
msgid "Are you sure you want to reset this setting?"
msgstr "您确定è¦é‡ç½®æ­¤è®¾ç½®å—?"
msgid "Select one or more hard drives to use and configure\n"
-msgstr "选择一个或多个硬盘驱动器æ¥ä½¿ç”¨å’Œé…ç½®\n"
+msgstr "选择è¦ä½¿ç”¨å’Œé…置的硬盘驱动器(å¯å¤šé€‰ï¼‰\n"
msgid "Any modifications to the existing setting will reset the disk layout!"
msgstr "对现有设置的任何修改都将é‡ç½®ç£ç›˜å¸ƒå±€ï¼"
msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
-msgstr "如果您é‡ç½®ç¡¬ç›˜é©±åŠ¨å™¨é€‰æ‹©ï¼Œè¿™ä¹Ÿå°†é‡ç½®å½“å‰ç£ç›˜å¸ƒå±€ã€‚ 你确定å—?"
+msgstr "如果é‡ç½®ç¡¬ç›˜é©±åŠ¨å™¨é€‰æ‹©ï¼Œåˆ™å½“å‰ç£ç›˜å¸ƒå±€ä¹Ÿå°†é‡ç½®ã€‚您确定å—?"
msgid "Save and exit"
msgstr "ä¿å­˜å¹¶é€€å‡º"
@@ -691,7 +691,7 @@ msgid ""
"contains queued partitions, this will remove those, are you sure?"
msgstr ""
"{}\n"
-"包å«æŽ’队的分区,这将删除那些,你确定å—?"
+"包å«æŽ’队的分区,这将删除这些分区,您确定å—?"
msgid "No audio server"
msgstr "没有音频æœåŠ¡å™¨"
@@ -700,7 +700,7 @@ msgid "(default)"
msgstr "(默认)"
msgid "Use ESC to skip"
-msgstr "按 ESC 键跳过"
+msgstr "按 ESC 跳过"
msgid ""
"Use CTRL+C to reset current selection\n"
@@ -708,28 +708,28 @@ msgid ""
msgstr "使用 CTRL+C å¯é‡ç½®å½“å‰é€‰é¡¹\n"
msgid "Copy to: "
-msgstr "å¤åˆ¶åˆ°ï¼š "
+msgstr "å¤åˆ¶åˆ°ï¼š"
msgid "Edit: "
-msgstr "编辑: "
+msgstr "编辑:"
msgid "Key: "
-msgstr "键: "
+msgstr "密钥:"
msgid "Edit {}: "
-msgstr "编辑 {}: "
+msgstr "编辑 {}:"
msgid "Add: "
-msgstr "添加: "
+msgstr "添加:"
msgid "Value: "
-msgstr "值: "
+msgstr "值:"
msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
-msgstr "您å¯ä»¥è·³è¿‡é€‰æ‹©é©±åŠ¨å™¨å’Œåˆ†åŒºå¹¶ä½¿ç”¨å®‰è£…在 /mnt 的任何驱动器设置(实验性)"
+msgstr "您å¯ä»¥è·³è¿‡é€‰æ‹©é©±åŠ¨å™¨å’Œåˆ†åŒºï¼Œå¹¶ä½¿ç”¨ä»»ä½•æŒ‚载在 /mnt 上的驱动器设置(实验性)"
msgid "Select one of the disks or skip and use /mnt as default"
-msgstr "选择其中一个ç£ç›˜æˆ–跳过并使用 /mnt 作为默认值"
+msgstr "选择一个ç£ç›˜æˆ–跳过并使用 /mnt 作为默认值"
msgid "Select which partitions to mark for formatting:"
msgstr "选择è¦æ ‡è®°ä¸ºæ ¼å¼åŒ–的分区:"
@@ -753,17 +753,16 @@ msgid "Either root-password or at least 1 user with sudo privileges must be spec
msgstr "必须指定 root 密ç æˆ–至少 1 个具有 sudo æƒé™çš„用户"
msgid "Enter username (leave blank to skip): "
-msgstr "输入用户å(留空跳过): "
+msgstr "输入用户å(留空跳过):"
msgid "The username you entered is invalid. Try again"
-msgstr "您输入的用户å无效。 å†è¯•ä¸€æ¬¡"
+msgstr "您输入的用户å无效。请é‡è¯•"
msgid "Should \"{}\" be a superuser (sudo)?"
-msgstr "å°† \"{}\" 设置为超级用户(sudo)å—?"
+msgstr "是å¦å°†â€œ{}â€è®¾ç½®ä¸ºè¶…级用户(sudo)?"
-#, fuzzy
msgid "Select which partitions to encrypt"
-msgstr "选择è¦åŠ å¯†çš„分区:"
+msgstr "选择è¦åŠ å¯†çš„分区"
msgid "very weak"
msgstr "éžå¸¸å¼±"
@@ -787,39 +786,38 @@ msgid "Delete subvolume"
msgstr "删除å­å·"
msgid "Configured {} interfaces"
-msgstr "å·²é…置的 {} 接å£"
+msgstr "å·²é…ç½® {} 个接å£"
msgid "This option enables the number of parallel downloads that can occur during installation"
-msgstr "此选项å¯ç”¨å®‰è£…期间å¯èƒ½å‘生的并行下载次数"
+msgstr "此选项å¯ç”¨å®‰è£…期间å¯ä»¥å‘生的并行下载数"
-#, python-brace-format
msgid ""
"Enter the number of parallel downloads to be enabled.\n"
-" (Enter a value between 1 to {max_downloads})\n"
+" (Enter a value between 1 to {})\n"
"Note:"
msgstr ""
"输入è¦å¯ç”¨çš„并行下载数。\n"
-" (输入一个介于 1 到 {max_downloads} 之间的值)\n"
+" (输入一个介于 1 到 {} 之间的值)\n"
"æ示:"
-msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )"
-msgstr " - 最大值:{max_downloads}(å…许 {max_downloads} 次并行下载,一次å…许 {max_downloads+1} 次下载)"
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - 最大值:{}(å…许 {} 个并行下载,å³åŒæ—¶å…许 {} 个下载)"
msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
-msgstr " - 最å°å€¼ï¼š1(å…许 1 次并行下载,一次å…许 2 次下载)"
+msgstr " - 最å°å€¼ï¼š1(å…许 1 个并行下载,åŒæ—¶å…许 2 个下载)"
msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
-msgstr " - ç¦ç”¨/默认:0(ç¦ç”¨å¹¶è¡Œä¸‹è½½ï¼Œä¸€æ¬¡åªå…许 1 个下载)"
+msgstr " - ç¦ç”¨/默认值:0(ç¦ç”¨å¹¶è¡Œä¸‹è½½ï¼ŒåŒæ—¶åªå…许 1 个下载)"
#, python-brace-format
msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
-msgstr "è¾“å…¥æ— æ•ˆï¼ ä½¿ç”¨æœ‰æ•ˆè¾“å…¥é‡è¯• [1 到 {max_downloads},或 0 到ç¦ç”¨]"
+msgstr "输入无效ï¼è¯·é‡è¯•ä¸€ä¸ªæœ‰æ•ˆè¾“å…¥ [1 到 {max_downloads},或 0 以ç¦ç”¨]"
msgid "Parallel Downloads"
msgstr "并行下载"
msgid "ESC to skip"
-msgstr "按 ESC 键跳过"
+msgstr "按 ESC 跳过"
msgid "CTRL+C to reset"
msgstr "按 CTRL+C é‡ç½®"
@@ -828,53 +826,423 @@ msgid "TAB to select"
msgstr "按 TAB 选择"
msgid "[Default value: 0] > "
-msgstr "[默认值: 0] > "
+msgstr "[默认值:0] > "
msgid "To be able to use this translation, please install a font manually that supports the language."
-msgstr "为了能够使用此翻译,请手动安装支æŒè¯¥è¯­è¨€çš„字体。"
+msgstr "è¦ä½¿ç”¨æ­¤ç¿»è¯‘,请手动安装支æŒè¯¥è¯­è¨€çš„字体。"
msgid "The font should be stored as {}"
msgstr "字体应存储为 {}"
-#, fuzzy
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall éœ€è¦ root æƒé™æ‰èƒ½è¿è¡Œã€‚有关更多信æ¯ï¼Œè¯·å‚阅 --help。"
+
+msgid "Select an execution mode"
+msgstr "选择执行模å¼"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "无法从指定的 URL 获å–é…置文件:{}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "é…置文件必须具有唯一的å称,但找到了具有é‡å¤å称的é…置文件定义:{}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "选择è¦ä½¿ç”¨å¹¶é…置的设备(å¯å¤šé€‰ï¼‰"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "如果é‡ç½®è®¾å¤‡é€‰æ‹©ï¼Œåˆ™å½“å‰ç£ç›˜å¸ƒå±€ä¹Ÿå°†é‡ç½®ã€‚您确定å—?"
+
+msgid "Existing Partitions"
+msgstr "现有分区"
+
+msgid "Select a partitioning option"
+msgstr "选择分区选项"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "输入已挂载设备的根目录:"
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "/home 分区的最å°å®¹é‡ä¸ºï¼š{}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Arch Linux 分区的最å°å®¹é‡ä¸ºï¼š{}GB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "这是预先编写的é…置文件列表,它们å¯èƒ½ä¼šä½¿å®‰è£…æ¡Œé¢çŽ¯å¢ƒç­‰å†…容更容易"
+
+msgid "Current profile selection"
+msgstr "当å‰é…置文件选择"
+
+msgid "Remove all newly added partitions"
+msgstr "删除所有新添加的分区"
+
+msgid "Assign mountpoint"
+msgstr "分é…挂载点"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "将分区标记/å–消标记为格å¼åŒ–(擦除数æ®ï¼‰"
+
+msgid "Mark/Unmark as bootable"
+msgstr "将分区标记/å–消标记为å¯å¼•å¯¼"
+
+msgid "Change filesystem"
+msgstr "更改文件系统"
+
+msgid "Mark/Unmark as compressed"
+msgstr "标记/å–消标记为压缩"
+
+msgid "Set subvolumes"
+msgstr "设置å­å·"
+
+msgid "Delete partition"
+msgstr "删除分区"
+
+msgid "Partition"
+msgstr "分区"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "此分区当å‰å·²åŠ å¯†ï¼Œè¦æ ¼å¼åŒ–它必须指定文件系统"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr "分区挂载点是相对于安装目标的目录内部的,例如引导分区(boot)的挂载点为 /boot。"
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "如果设置了挂载点 /boot,则该分区也将被标记为å¯å¼•å¯¼ã€‚"
+
+msgid "Mountpoint: "
+msgstr "挂载点:"
+
+msgid "Current free sectors on device {}:"
+msgstr "设备 {} 上当å‰å¯ç”¨çš„扇区:"
+
+msgid "Total sectors: {}"
+msgstr "总扇区数:{}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "输入起始扇区(百分比或å—å·ï¼Œé»˜è®¤ï¼š{}): "
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "输入分区的结æŸæ‰‡åŒºï¼ˆç™¾åˆ†æ¯”或å—å·ï¼Œé»˜è®¤ï¼š{}):"
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "这将删除所有新添加的分区,是å¦ç»§ç»­ï¼Ÿ"
+
+msgid "Partition management: {}"
+msgstr "分区管ç†ï¼š{}"
+
+msgid "Total length: {}"
+msgstr "总长度:{}"
+
msgid "Encryption type"
-msgstr "加密密ç "
+msgstr "加密类型"
msgid "Partitions"
-msgstr ""
+msgstr "分区"
msgid "No HSM devices available"
-msgstr ""
+msgstr "没有å¯ç”¨çš„ HSM 设备"
-#, fuzzy
msgid "Partitions to be encrypted"
-msgstr "选择è¦åŠ å¯†çš„分区:"
+msgstr "è¦åŠ å¯†çš„分区"
msgid "Select disk encryption option"
-msgstr ""
+msgstr "选择ç£ç›˜åŠ å¯†é€‰é¡¹"
msgid "Select a FIDO2 device to use for HSM"
-msgstr ""
+msgstr "选择è¦ç”¨äºŽ HSM çš„ FIDO2 设备"
+
+msgid "Use a best-effort default partition layout"
+msgstr "使用最佳的默认分区布局"
+
+msgid "Manual Partitioning"
+msgstr "手动分区"
+
+msgid "Pre-mounted configuration"
+msgstr "预挂载é…ç½®"
+
+msgid "Unknown"
+msgstr "未知"
+
+msgid "Partition encryption"
+msgstr "分区加密"
+
+msgid " ! Formatting {} in "
+msgstr " ! 正在格å¼åŒ– {} 为 "
+
+msgid "↠Back"
+msgstr "↠返回"
+
+msgid "Disk encryption"
+msgstr "ç£ç›˜åŠ å¯†"
+
+msgid "Configuration"
+msgstr "é…ç½®"
+
+msgid "Password"
+msgstr "密ç "
-#, fuzzy
msgid "All settings will be reset, are you sure?"
-msgstr "{} 包å«æŽ’队分区,这将删除这些分区,您确定å—?"
+msgstr "所有设置将被é‡ç½®ï¼Œæ‚¨ç¡®å®šå—?"
msgid "Back"
+msgstr "返回"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "请选择è¦ä¸ºæ‰€é€‰é…置文件安装的登录管ç†å™¨ï¼š{}"
+
+msgid "Environment type: {}"
+msgstr "环境类型:{}"
+
+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 "Sway ä¸æ”¯æŒ Nvidia 的专有驱动。您å¯èƒ½ä¼šé‡åˆ°é—®é¢˜ï¼Œæ‚¨ç¡®å®šè¦ç»§ç»­å—?"
+
+msgid "Installed packages"
+msgstr "已安装的软件包"
+
+msgid "Add profile"
+msgstr "添加é…置文件"
+
+msgid "Edit profile"
+msgstr "编辑é…置文件"
+
+msgid "Delete profile"
+msgstr "删除é…置文件"
+
+msgid "Profile name: "
+msgstr "é…置文件å称:"
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "您输入的é…置文件å称已被使用。请é‡è¯•"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "è¦ä¸Žæ­¤é…置文件一åŒå®‰è£…的软件包(空格分隔,留空跳过):"
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "è¦ä¸Žæ­¤é…置文件一åŒå¯ç”¨çš„æœåŠ¡ï¼ˆç©ºæ ¼åˆ†éš”,留空跳过):"
+
+msgid "Should this profile be enabled for installation?"
+msgstr "是å¦å¯ç”¨æ­¤é…置文件进行安装?"
+
+msgid "Create your own"
+msgstr "创建您自己的"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
msgstr ""
+"\n"
+"\n"
+"选择一个显å¡é©±åŠ¨ç¨‹åºï¼Œæˆ–留空以安装所有开æºé©±åŠ¨ç¨‹åº"
-msgid "Disk encryption"
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway 需è¦è®¿é—®æ‚¨çš„ seat(硬件设备的集åˆï¼Œä¾‹å¦‚键盘ã€é¼ æ ‡ç­‰ï¼‰"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
msgstr ""
+"\n"
+"\n"
+"选择一个选项æ¥ç»™ Sway æ供对您硬件的访问æƒé™"
+
+msgid "Graphics driver"
+msgstr "显å¡é©±åŠ¨ç¨‹åº"
+
+msgid "Greeter"
+msgstr "登录管ç†å™¨"
+
+msgid "Please chose which greeter to install"
+msgstr "请选择è¦å®‰è£…的登录管ç†å™¨"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "这份列表列出了预编写的默认é…置文件"
+
+msgid "Disk configuration"
+msgstr "ç£ç›˜é…ç½®"
+
+msgid "Profiles"
+msgstr "é…置文件"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "正在查找å¯èƒ½ç”¨äºŽä¿å­˜é…置文件的目录 ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "选择一个或多个目录ä¿å­˜é…置文件"
+
+msgid "Add a custom mirror"
+msgstr "添加自定义镜åƒ"
+
+msgid "Change custom mirror"
+msgstr "更改自定义镜åƒ"
+
+msgid "Delete custom mirror"
+msgstr "删除自定义镜åƒ"
+
+msgid "Enter name (leave blank to skip): "
+msgstr "输入用户å(留空跳过):"
+
+msgid "Enter url (leave blank to skip): "
+msgstr "输入网å€ï¼ˆç•™ç©ºè·³è¿‡ï¼‰ï¼š"
+
+msgid "Select signature check option"
+msgstr "选择签å检查选项"
+
+msgid "Select signature option"
+msgstr "选择签å选项"
+
+msgid "Custom mirrors"
+msgstr "自定义镜åƒ"
+
+msgid "Defined"
+msgstr "已定义"
+
+msgid "Save user configuration (including disk layout)"
+msgstr "ä¿å­˜ç”¨æˆ·é…置(包括ç£ç›˜å¸ƒå±€ï¼‰"
+
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr ""
+"输入è¦ä¿å­˜é…置的目录(å¯æŒ‰ TAB 补全)\n"
+"ä¿å­˜ç›®å½•ï¼š"
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+"您想将 {} 个é…置文件ä¿å­˜åœ¨ä»¥ä¸‹ä½ç½®å—?\n"
+"\n"
+"{}"
+
+msgid "Saving {} configuration files to {}"
+msgstr "正在将 {} é…置文件ä¿å­˜åˆ° {}"
+
+msgid "Mirrors"
+msgstr "é•œåƒ"
+
+msgid "Mirror regions"
+msgstr "é•œåƒåŒºåŸŸ"
+
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - 最大值:{}(å…许 {} 个并行下载,æ¯æ¬¡å…许 {max_downloads+1} 个下载)"
+
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "输入无效ï¼è¯·é‡è¯•ä¸€ä¸ªæœ‰æ•ˆè¾“å…¥ [1 到 {},或 0 以ç¦ç”¨]"
+
+msgid "Locales"
+msgstr "区域设置"
+
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "使用 NetworkManager(在 GNOME å’Œ KDE 中以图形方å¼é…置互è”网所必需)"
+
+msgid "Total: {} / {}"
+msgstr "总长度:{} / {}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr "所有输入的值都å¯ä»¥åŽç¼€ä¸€ä¸ªå•ä½ï¼šBã€KBã€KiBã€MBã€MiB……"
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr "若没有指定å•ä½ï¼Œåˆ™å€¼è¢«ä½œä¸ºæ‰‡åŒºå—å·"
+
+msgid "Enter start (default: sector {}): "
+msgstr "输入起始扇区ä½ç½®ï¼ˆé»˜è®¤ï¼šæ‰‡åŒº {}): "
+
+msgid "Enter end (default: {}): "
+msgstr "输入末尾扇区ä½ç½®ï¼ˆé»˜è®¤ï¼š{}): "
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr "无法确定 fido2 设备。libfido2 是å¦å·²å®‰è£…?"
+
+msgid "Path"
+msgstr "路径"
+
+msgid "Manufacturer"
+msgstr "制造商"
+
+msgid "Product"
+msgstr "产å“"
+
+#, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "无效的é…置:{error}"
+
+msgid "Type"
+msgstr "类型"
+
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "此选项å¯ç”¨è½¯ä»¶åŒ…下载期间å¯ä»¥å‘生的并行下载数"
+
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"输入è¦å¯ç”¨çš„并行下载数。\n"
+"\n"
+"æ示:\n"
+
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - 最大推è值:{}(æ¯æ¬¡åŒæ—¶å…许 {} 个并行下载)"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - ç¦ç”¨/默认值:0(ç¦ç”¨å¹¶è¡Œä¸‹è½½ï¼ŒåŒæ—¶åªå…许 1 个下载)\n"
+
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "输入无效ï¼è¯·é‡è¯•æœ‰æ•ˆè¾“å…¥ [或 0 以ç¦ç”¨]"
+
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Hyprland 需è¦è®¿é—®æ‚¨çš„ seat(硬件设备的集åˆï¼Œä¾‹å¦‚键盘ã€é¼ æ ‡ç­‰ï¼‰"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"选择一个选项æ¥ç»™ Hyprland æ供对您硬件的访问æƒé™"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr "所有输入的值都å¯ä»¥åŽç¼€ä¸€ä¸ªå•ä½ï¼š%ã€Bã€KBã€KiBã€MBã€MiB……"
#, fuzzy
-msgid "Password"
-msgstr "Root 密ç "
+msgid "Would you like to use unified kernel images?"
+msgstr "您想在 zram 上使用交æ¢åˆ†åŒºï¼ˆswap)å—?"
-msgid "Partition encryption"
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
msgstr ""
-#~ msgid "Enter the start sector (percentage or block number, default: {}): "
-#~ msgstr "输入起始扇区(百分比或å—å·ï¼Œé»˜è®¤ï¼š{}): "
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "删除é…置文件"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
-#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): "
-#~ msgstr "输入分区的结æŸæ‰‡åŒºï¼ˆç™¾åˆ†æ¯”或å—å·ï¼Œä¾‹å¦‚:{}): "
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "将分区标记/å–消标记为å¯å¼•å¯¼"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "您想使用 BTRFS 压缩å—?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo
new file mode 100644
index 00000000..15491b88
--- /dev/null
+++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo
Binary files differ
diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po
new file mode 100644
index 00000000..7f98e3f6
--- /dev/null
+++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po
@@ -0,0 +1,1268 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Xuan-Rui Fan <serfinxx@gmail.com>\n"
+"Language-Team: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.1.1\n"
+
+msgid "[!] A log file has been created here: {} {}"
+msgstr "[!] 日誌文件已在此處創建:{} {}"
+
+msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"
+msgstr " 請將此å•é¡Œï¼ˆä»¥åŠæ–‡ä»¶ï¼‰æ交到 https://github.com/archlinux/archinstall/issues"
+
+msgid "Do you really want to abort?"
+msgstr "您真的è¦ä¸­æ­¢å—Žï¼Ÿ"
+
+msgid "And one more time for verification: "
+msgstr "è«‹å†è¼¸å…¥ä¸€æ¬¡ä»¥é€²è¡Œé©—證:"
+
+msgid "Would you like to use swap on zram?"
+msgstr "您想在 zram 上使用交æ›åˆ†å‰²å€ï¼ˆswap)嗎?"
+
+msgid "Desired hostname for the installation: "
+msgstr "請輸入安è£å¾Œé æœŸä½¿ç”¨çš„主機å稱(hostname):"
+
+msgid "Username for required superuser with sudo privileges: "
+msgstr "請輸入需è¦è¶…級使用者的使用者å稱(sudo 權é™ï¼‰ï¼š"
+
+msgid "Any additional users to install (leave blank for no users): "
+msgstr "è¦æ–°å¢žçš„其他使用者(留空表示ä¸æ–°å»ºå…¶ä»–使用者):"
+
+msgid "Should this user be a superuser (sudoer)?"
+msgstr "這個使用者應該æˆç‚ºè¶…級使用者(sudoer)嗎?"
+
+msgid "Select a timezone"
+msgstr "é¸æ“‡æ™‚å€"
+
+msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?"
+msgstr "您希望使用 GRUB 作為開機引導程å¼ï¼Œè€Œä¸æ˜¯ systemd-boot 嗎?"
+
+msgid "Choose a bootloader"
+msgstr "é¸æ“‡å¼•å°Žç¨‹å¼"
+
+msgid "Choose an audio server"
+msgstr "è«‹é¸æ“‡éŸ³è¨Šä¼ºæœå™¨"
+
+msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed."
+msgstr "僅安è£åŸºæœ¬å¥—件,如 baseã€base-develã€linuxã€linux-firmwareã€efibootmgr å’Œé¸æ“‡æ€§çš„軟體設定套件。"
+
+msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt."
+msgstr "如果您想è¦ä¸€å€‹ç¶²é ç€è¦½å™¨ï¼Œä¾‹å¦‚ firefox 或 chromium,å¯ä»¥åœ¨ä¸‹é¢çš„æ示字元中指定。"
+
+msgid "Write additional packages to install (space separated, leave blank to skip): "
+msgstr "請輸入您è¦å®‰è£çš„其它套件(以空格分隔,留空以跳éŽï¼‰ï¼š"
+
+msgid "Copy ISO network configuration to installation"
+msgstr "å°‡ ISO 中的網路設置複製到安è£ä¸­"
+
+msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)"
+msgstr "使用 NetworkManager(在 GNOME å’Œ KDE é€éŽåœ–形界é¢è¨­ç½®ç¶²éš›ç¶²è·¯é€£ç·šæ‰€éœ€ï¼‰"
+
+msgid "Select one network interface to configure"
+msgstr "è«‹é¸æ“‡è¦è¨­å®šçš„網路介é¢"
+
+msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\""
+msgstr "è«‹é¸æ“‡è¦ç‚ºâ€œ{}â€è¨­å®šçš„模å¼æˆ–直接使用é è¨­æ¨¡å¼â€œ{}â€"
+
+msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): "
+msgstr "請輸入 {} çš„ IP å’Œå­ç¶²è·¯ï¼ˆä¾‹å¦‚:192.168.0.5/24):"
+
+msgid "Enter your gateway (router) IP address or leave blank for none: "
+msgstr "請輸入您的網關(路由器)IP 地å€æˆ–留空以跳éŽï¼š"
+
+msgid "Enter your DNS servers (space separated, blank for none): "
+msgstr "請輸入您的 DNS 伺æœå™¨ï¼ˆä»¥ç©ºæ ¼åˆ†éš”,留空以跳éŽï¼‰ï¼š"
+
+msgid "Select which filesystem your main partition should use"
+msgstr "è«‹é¸æ“‡æ‚¨çš„主è¦åˆ†å‰²å€æ‡‰è©²ä½¿ç”¨å“ªç¨®æª”案系統。"
+
+msgid "Current partition layout"
+msgstr "ç›®å‰çš„分割å€é…ç½®"
+
+msgid ""
+"Select what to do with\n"
+"{}"
+msgstr ""
+"è«‹é¸æ“‡è¦åŸ·è¡Œçš„æ“作\n"
+"{}"
+
+msgid "Enter a desired filesystem type for the partition"
+msgstr "請輸入您想è¦çš„分割å€æª”案系統類型"
+
+msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): "
+msgstr "請輸入起始ä½ç½®ï¼ˆä»¥ parted å–®ä½è¡¨ç¤ºï¼šsã€GBã€% 等等;é è¨­å€¼ï¼š{}):"
+
+msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): "
+msgstr "請輸入çµæŸä½ç½®ï¼ˆä»¥ parted å–®ä½è¡¨ç¤ºï¼šsã€GBã€% 等等;例如:{}):"
+
+msgid "{} contains queued partitions, this will remove those, are you sure?"
+msgstr "{} 包å«ä½‡åˆ—分割å€ï¼Œé€™å°‡åˆªé™¤é€™äº›åˆ†å‰²å€ï¼Œæ‚¨ç¢ºå®šå—Žï¼Ÿ"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partitions to delete"
+msgstr ""
+"{}\n"
+"\n"
+"請根據索引é¸æ“‡æ¬²åˆªé™¤çš„分割å€"
+
+msgid ""
+"{}\n"
+"\n"
+"Select by index which partition to mount where"
+msgstr ""
+"{}\n"
+"\n"
+"è«‹ä¾æ“šç´¢å¼•é¸æ“‡è¦æŽ›è¼‰çš„分割å€ä»¥åŠæŽ›è¼‰é»ž"
+
+msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * 分割å€æŽ›è¼‰é»žæ˜¯ç›¸å°æ–¼å®‰è£å…§éƒ¨çš„,例如 boot 應該為 /boot。"
+
+msgid "Select where to mount partition (leave blank to remove mountpoint): "
+msgstr "è«‹é¸æ“‡æŽ›è¼‰åˆ†å‰²å€çš„ä½ç½®ï¼ˆç•™ç©ºä»¥ç§»é™¤æŽ›è¼‰é»žï¼‰ï¼š"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mask for formatting"
+msgstr ""
+"{}\n"
+"\n"
+"è«‹é¸æ“‡è¦æ ¼å¼åŒ–的分割å€"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as encrypted"
+msgstr ""
+"{}\n"
+"\n"
+"è«‹é¸æ“‡è¦åŠ å¯†çš„分割å€"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to mark as bootable"
+msgstr ""
+"{}\n"
+"\n"
+"è«‹é¸æ“‡è¦æ¨™è¨˜ç‚ºå¯å¼•å°Žçš„分割å€"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set a filesystem on"
+msgstr ""
+"{}\n"
+"\n"
+"è«‹é¸æ“‡è¦è¨­ç½®ç‚ºæ–‡ä»¶ç³»çµ±çš„分割å€"
+
+msgid "Enter a desired filesystem type for the partition: "
+msgstr "為分å€é¸æ“‡æ‰€éœ€çš„文件系統類型:"
+
+msgid "Archinstall language"
+msgstr "Archinstall 語言"
+
+msgid "Wipe all selected drives and use a best-effort default partition layout"
+msgstr "移除所有é¸å®šçš„硬碟並使用最佳的é è¨­åˆ†å‰²ä½ˆå±€"
+
+msgid "Select what to do with each individual drive (followed by partition usage)"
+msgstr "ä¾åºé¸æ“‡ç¡¬ç¢Ÿï¼ˆä¸¦è¨­å®šåˆ†å‰²ä½ˆå±€ï¼‰"
+
+msgid "Select what you wish to do with the selected block devices"
+msgstr "é¸æ“‡æ‚¨å¸Œæœ›å°æ‰€é¸å€å¡Šè£ç½®åŸ·è¡Œçš„æ“作"
+
+msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments"
+msgstr "以下是é ç·¨ç¨‹è¨­ç½®æª”案的列表,它們å¯ä»¥ä½¿å®‰è£æ¡Œé¢ç’°å¢ƒç­‰æ›´åŠ å®¹æ˜“"
+
+msgid "Select keyboard layout"
+msgstr "é¸æ“‡éµç›¤ä½ˆå±€"
+
+msgid "Select one of the regions to download packages from"
+msgstr "è«‹é¸æ“‡ä¸€å€‹åœ°å€ä»¥ä¸‹è¼‰è»Ÿé«”套件"
+
+msgid "Select one or more hard drives to use and configure"
+msgstr "é¸æ“‡è¦ä½¿ç”¨å’Œè¨­å®šçš„硬碟(å¯å¤šé¸ï¼‰"
+
+msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options."
+msgstr "為了與您的 AMD 設備實ç¾æœ€ä½³å…¼å®¹æ€§ï¼Œæ‚¨å¯èƒ½éœ€è¦ä½¿ç”¨é–‹æºæˆ– 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 "為了與您的 Intel 設備實ç¾æœ€ä½³å…¼å®¹æ€§ï¼Œæ‚¨å¯èƒ½éœ€è¦ä½¿ç”¨é–‹æºæˆ– Intel é¸é …。\n"
+
+msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n"
+msgstr "為了與您的 Nvidia 設備實ç¾æœ€ä½³å…¼å®¹æ€§ï¼Œæ‚¨å¯èƒ½éœ€è¦ä½¿ç”¨ Nvidia 官方驅動程å¼ã€‚\n"
+
+msgid ""
+"\n"
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"é¸æ“‡åœ–形驅動程å¼æˆ–留空以安è£é–‹æºé©…動程å¼"
+
+msgid "All open-source (default)"
+msgstr "全部開æºï¼ˆé»˜èªï¼‰"
+
+msgid "Choose which kernels to use or leave blank for default \"{}\""
+msgstr "é¸æ“‡è¦ä½¿ç”¨çš„内核或留空以使用默èªå€¼â€œ{}â€"
+
+msgid "Choose which locale language to use"
+msgstr "é¸æ“‡è¦ä½¿ç”¨çš„å€åŸŸèªžè¨€"
+
+msgid "Choose which locale encoding to use"
+msgstr "é¸æ“‡è¦ä½¿ç”¨çš„å€åŸŸç·¨ç¢¼"
+
+msgid "Select one of the values shown below: "
+msgstr "é¸æ“‡å¦‚下所示的值之一:"
+
+msgid "Select one or more of the options below: "
+msgstr "é¸æ“‡ä»¥ä¸‹ä¸€å€‹æˆ–多個é¸é …:"
+
+msgid "Adding partition...."
+msgstr "新增分割å€...."
+
+msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."
+msgstr "您需è¦è¼¸å…¥æœ‰æ•ˆçš„文件系統類型æ‰èƒ½ç¹¼çºŒã€‚有關有效的文件系統類型,請åƒé–± `man parted`。"
+
+msgid "Error: Listing profiles on URL \"{}\" resulted in:"
+msgstr "錯誤:在 URL “{}†上列出設定檔案時出錯:"
+
+msgid "Error: Could not decode \"{}\" result as JSON:"
+msgstr "錯誤:無法將“{}â€çµæžœè§£ç¢¼ç‚º JSON:"
+
+msgid "Keyboard layout"
+msgstr "éµç›¤ä½ˆå±€"
+
+msgid "Mirror region"
+msgstr "é¡åƒå€åŸŸ"
+
+msgid "Locale language"
+msgstr "å€åŸŸèªžè¨€"
+
+msgid "Locale encoding"
+msgstr "å€åŸŸç·¨ç¢¼"
+
+msgid "Drive(s)"
+msgstr "硬碟"
+
+msgid "Disk layout"
+msgstr "ç£ç›¤ä½ˆå±€"
+
+msgid "Encryption password"
+msgstr "加密密碼"
+
+msgid "Swap"
+msgstr "交æ›ç©ºé–“"
+
+msgid "Bootloader"
+msgstr "引導加載程å¼"
+
+msgid "Root password"
+msgstr "Root 密碼"
+
+msgid "Superuser account"
+msgstr "超級使用者帳戶"
+
+msgid "User account"
+msgstr "使用者帳戶"
+
+msgid "Profile"
+msgstr "設定檔案"
+
+msgid "Audio"
+msgstr "音訊"
+
+msgid "Kernels"
+msgstr "内核"
+
+msgid "Additional packages"
+msgstr "é¡å¤–套件"
+
+msgid "Network configuration"
+msgstr "網路設置"
+
+msgid "Automatic time sync (NTP)"
+msgstr "自動åŒæ­¥æ™‚é–“ (NTP)"
+
+msgid "Install ({} config(s) missing)"
+msgstr "安è£ï¼ˆç¼ºå°‘ {} 個é…置)"
+
+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 ""
+"您決定跳éŽç¡¬ç¢Ÿé¸æ“‡\n"
+"並將使用掛載在 {}(實驗性)上任一的硬碟設置\n"
+"警告:Archinstall ä¸æœƒæª¢æŸ¥æ­¤è¨­ç½®çš„é©ç”¨æ€§\n"
+"您想繼續嗎?"
+
+msgid "Re-using partition instance: {}"
+msgstr "é‡è¤‡ä½¿ç”¨åˆ†å‰²å€å¯¦ä¾‹ï¼š{}"
+
+msgid "Create a new partition"
+msgstr "創建新分割å€"
+
+msgid "Delete a partition"
+msgstr "刪除一個分割å€"
+
+msgid "Clear/Delete all partitions"
+msgstr "清除/刪除所有分割å€"
+
+msgid "Assign mount-point for a partition"
+msgstr "為分割å€åˆ†é…掛載點"
+
+msgid "Mark/Unmark a partition to be formatted (wipes data)"
+msgstr "標記/å–消標記è¦æ ¼å¼åŒ–的分割å€ï¼ˆç§»é™¤æ•¸æ“šï¼‰"
+
+msgid "Mark/Unmark a partition as encrypted"
+msgstr "將分割å€æ¨™è¨˜/å–消標記為加密"
+
+msgid "Mark/Unmark a partition as bootable (automatic for /boot)"
+msgstr "將分割å€æ¨™è¨˜/å–消標記為å¯å¼•å¯¼ï¼ˆè‡ªå‹•ç‚º /boot)"
+
+msgid "Set desired filesystem for a partition"
+msgstr "為分割å€è¨­ç½®æ‰€éœ€çš„文件系統"
+
+msgid "Abort"
+msgstr "中止"
+
+msgid "Hostname"
+msgstr "主機å稱"
+
+msgid "Not configured, unavailable unless setup manually"
+msgstr "未設置,除éžæ‰‹å‹•è¨­ç½®ï¼Œå¦å‰‡ä¸å¯ç”¨"
+
+msgid "Timezone"
+msgstr "時å€"
+
+msgid "Set/Modify the below options"
+msgstr "請設置/修改以下é¸é …"
+
+msgid "Install"
+msgstr "安è£"
+
+msgid ""
+"Use ESC to skip\n"
+"\n"
+msgstr ""
+"按 ESC éµä»¥è·³éŽ\n"
+"\n"
+
+msgid "Suggest partition layout"
+msgstr "建議分割å€ä½ˆå±€"
+
+msgid "Enter a password: "
+msgstr "輸入密碼:"
+
+msgid "Enter a encryption password for {}"
+msgstr "輸入 {} 的加密密碼"
+
+msgid "Enter disk encryption password (leave blank for no encryption): "
+msgstr "輸入硬碟加密密碼(留空表示ä¸åŠ å¯†ï¼‰ï¼š"
+
+msgid "Create a required super-user with sudo privileges: "
+msgstr "創建具有 sudo 權é™çš„超級使用者:"
+
+msgid "Enter root password (leave blank to disable root): "
+msgstr "輸入 root 密碼(留空以ç¦ç”¨ root):"
+
+msgid "Password for user \"{}\": "
+msgstr "使用者“{}â€çš„密碼:"
+
+msgid "Verifying that additional packages exist (this might take a few seconds)"
+msgstr "驗證是å¦å­˜åœ¨å…¶å®ƒå¥—件(這å¯èƒ½éœ€è¦å¹¾ç§’é˜ï¼‰"
+
+msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n"
+msgstr "是å¦è¦å°é»˜èªæ™‚間伺æœå™¨ä½¿ç”¨è‡ªå‹•æ™‚é–“åŒæ­¥ (NTP)?\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 ""
+"為了使 NTP é‹ä½œï¼Œå¯èƒ½éœ€è¦ç¡¬é«”執行時間和其它設置後步驟。\n"
+"有關更多資訊,請查看 Arch wiki"
+
+msgid "Enter a username to create an additional user (leave blank to skip): "
+msgstr "輸入用å以創建其他使用者(留空以跳éŽï¼‰ï¼š"
+
+msgid "Use ESC to skip\n"
+msgstr "按 ESC éµä»¥è·³éŽ\n"
+
+msgid ""
+"\n"
+" Choose an object from the list, and select one of the available actions for it to execute"
+msgstr ""
+"\n"
+"從列表中é¸æ“‡ä¸€å€‹å°è±¡ï¼Œä¸¦é¸æ“‡è¦åŸ·è¡Œçš„æ“作"
+
+msgid "Cancel"
+msgstr "å–消"
+
+msgid "Confirm and exit"
+msgstr "確èªä¸¦é€€å‡º"
+
+msgid "Add"
+msgstr "新增"
+
+msgid "Copy"
+msgstr "複製"
+
+msgid "Edit"
+msgstr "編輯"
+
+msgid "Delete"
+msgstr "刪除"
+
+msgid "Select an action for '{}'"
+msgstr "為“{}â€é¸æ“‡ä¸€å€‹æ“作"
+
+msgid "Copy to new key:"
+msgstr "複製到新密鑰:"
+
+msgid "Unknown nic type: {}. Possible values are {}"
+msgstr "未知網å¡é¡žåž‹ï¼š{}。 å¯èƒ½çš„值為 {}"
+
+msgid ""
+"\n"
+"This is your chosen configuration:"
+msgstr ""
+"\n"
+"這是您é¸æ“‡çš„é…置:"
+
+msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate."
+msgstr "Pacman 已經在é‹è¡Œï¼Œæœ€å¤šç­‰å€™ 10 分é˜ç›´åˆ°å…¶çµ‚止。"
+
+msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."
+msgstr "é å…ˆå­˜åœ¨çš„ pacman 鎖從未退出。請在使用 archinstall 之å‰æ¸…ç†æ‰€æœ‰ç¾æœ‰çš„ pacman 工作階段。"
+
+msgid "Choose which optional additional repositories to enable"
+msgstr "é¸æ“‡è¦å•Ÿç”¨çš„å¯é¸é™„加庫"
+
+msgid "Add a user"
+msgstr "新增一個使用者"
+
+msgid "Change password"
+msgstr "修改密碼"
+
+msgid "Promote/Demote user"
+msgstr "å‡ç´š/é™ç´šä½¿ç”¨è€…"
+
+msgid "Delete User"
+msgstr "刪除使用者"
+
+msgid ""
+"\n"
+"Define a new user\n"
+msgstr ""
+"\n"
+"定義一個新使用者\n"
+
+msgid "User Name : "
+msgstr "用戶å:"
+
+msgid "Should {} be a superuser (sudoer)?"
+msgstr "是å¦å°‡{}設置为超級使用者(sudoer)?"
+
+msgid "Define users with sudo privilege: "
+msgstr "定義具有 sudo 權é™çš„使用者:"
+
+msgid "No network configuration"
+msgstr "無網路é…ç½®"
+
+msgid "Set desired subvolumes on a btrfs partition"
+msgstr "在 btrfs 分割å€ä¸Šè¨­ç½®æ‰€éœ€çš„å­å·"
+
+msgid ""
+"{}\n"
+"\n"
+"Select which partition to set subvolumes on"
+msgstr ""
+"{}\n"
+"\n"
+"é¸æ“‡è¦åœ¨å“ªå€‹åˆ†å‰²å€ä¸Šè¨­ç½®å­å·"
+
+msgid "Manage btrfs subvolumes for current partition"
+msgstr "管ç†ç•¶å‰åˆ†å‰²å€çš„ btrfs å­å·"
+
+msgid "No configuration"
+msgstr "ç„¡é…ç½®"
+
+msgid "Save user configuration"
+msgstr "ä¿å­˜ä½¿ç”¨è€…é…ç½®"
+
+msgid "Save user credentials"
+msgstr "ä¿å­˜ä½¿ç”¨è€…憑證"
+
+msgid "Save disk layout"
+msgstr "ä¿å­˜ç¡¬ç¢Ÿä½ˆå±€"
+
+msgid "Save all"
+msgstr "全部ä¿å­˜"
+
+msgid "Choose which configuration to save"
+msgstr "é¸æ“‡è¦ä¿å­˜çš„é…ç½®"
+
+msgid "Enter a directory for the configuration(s) to be saved: "
+msgstr "輸入è¦ä¿å­˜é…置的目錄:"
+
+msgid "Not a valid directory: {}"
+msgstr "ä¸æ˜¯æœ‰æ•ˆçš„目錄:{}"
+
+msgid "The password you are using seems to be weak,"
+msgstr "您使用的密碼似乎很弱,"
+
+msgid "are you sure you want to use it?"
+msgstr "您確定è¦ä½¿ç”¨å®ƒå—Žï¼Ÿ"
+
+msgid "Optional repositories"
+msgstr "å¯é¸ç›®éŒ„"
+
+msgid "Save configuration"
+msgstr "ä¿å­˜é…ç½®"
+
+msgid "Missing configurations:\n"
+msgstr "缺少é…ç½®:\n"
+
+msgid "Either root-password or at least 1 superuser must be specified"
+msgstr "必須指定 root 密碼或至少 1 個超級使用者"
+
+msgid "Manage superuser accounts: "
+msgstr "管ç†è¶…級使用者帳號:"
+
+msgid "Manage ordinary user accounts: "
+msgstr "管ç†æ™®é€šä½¿ç”¨è€…帳號:"
+
+msgid " Subvolume :{:16}"
+msgstr " å­å· :{:16}"
+
+msgid " mounted at {:16}"
+msgstr " 掛載在 {:16}"
+
+msgid " with option {}"
+msgstr " 與é¸é … {}"
+
+msgid ""
+"\n"
+" Fill the desired values for a new subvolume \n"
+msgstr ""
+"\n"
+" 填寫新å­å·æ‰€éœ€çš„值\n"
+
+msgid "Subvolume name "
+msgstr "å­å·å"
+
+msgid "Subvolume mountpoint"
+msgstr "å­å·æŽ›è¼‰é»ž"
+
+msgid "Subvolume options"
+msgstr "å­å·é¸é …"
+
+msgid "Save"
+msgstr "ä¿å­˜"
+
+msgid "Subvolume name :"
+msgstr "å­å·å:"
+
+msgid "Select a mount point :"
+msgstr "é¸æ“‡æŽ›è¼‰é»žï¼š"
+
+msgid "Select the desired subvolume options "
+msgstr "é¸æ“‡æ‰€éœ€çš„å­å·é¸é …"
+
+msgid "Define users with sudo privilege, by username: "
+msgstr "以用戶å定義具有 sudo 權é™çš„使用者:"
+
+msgid "[!] A log file has been created here: {}"
+msgstr "[!] 日誌文件已在此處創建:{}"
+
+msgid "Would you like to use BTRFS subvolumes with a default structure?"
+msgstr "您想使用具有默èªçµæ§‹çš„ BTRFS å­å·å—Žï¼Ÿ"
+
+msgid "Would you like to use BTRFS compression?"
+msgstr "您想使用 BTRFS 壓縮嗎?"
+
+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"
+
+msgid "Minimum capacity for /home partition: {}GB\n"
+msgstr "/home 分割å€çš„最å°å®¹é‡ç‚ºï¼š{}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GB"
+msgstr "Arch Linux 分割å€çš„最å°å®¹é‡ç‚ºï¼š{}GB"
+
+msgid "Continue"
+msgstr "繼續"
+
+msgid "yes"
+msgstr "是"
+
+msgid "no"
+msgstr "å¦"
+
+msgid "set: {}"
+msgstr "設置:{}"
+
+msgid "Manual configuration setting must be a list"
+msgstr "手動é…置設置必須為一個列表"
+
+msgid "No iface specified for manual configuration"
+msgstr "沒有為手動é…置指定 iface"
+
+msgid "Manual nic configuration with no auto DHCP requires an IP address"
+msgstr "沒有自動 DHCP 的手動 nic é…ç½®éœ€è¦ IP 地å€"
+
+msgid "Add interface"
+msgstr "新增接å£"
+
+msgid "Edit interface"
+msgstr "編輯接å£"
+
+msgid "Delete interface"
+msgstr "刪除接å£"
+
+msgid "Select interface to add"
+msgstr "é¸æ“‡è¦æ–°å¢žçš„接å£"
+
+msgid "Manual configuration"
+msgstr "手動é…ç½®"
+
+msgid "Mark/Unmark a partition as compressed (btrfs only)"
+msgstr "將分割å€æ¨™è¨˜/å–æ¶ˆæ¨™è¨˜ç‚ºå£“ç¸®ï¼ˆåƒ…é™ btrfs)"
+
+msgid "The password you are using seems to be weak, are you sure you want to use it?"
+msgstr "您使用的密碼似乎很弱,您確定è¦ä½¿ç”¨å—Žï¼Ÿ"
+
+msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway"
+msgstr "æ供一系列桌é¢ç’°å¢ƒå’Œå¹³é‹ªè¦–窗管ç†å™¨ï¼Œä¾‹å¦‚ gnome, kde, sway"
+
+msgid "Select your desired desktop environment"
+msgstr "é¸æ“‡æ‚¨æƒ³è¦çš„æ¡Œé¢ç’°å¢ƒ"
+
+msgid "A very basic installation that allows you to customize Arch Linux as you see fit."
+msgstr "一個éžå¸¸åŸºæœ¬çš„安è£ï¼Œå…許您根據需è¦è‡ªå®šç¾© Arch Linux。"
+
+msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb"
+msgstr "æ供一系列å¯ä¾›å®‰è£å’Œå•Ÿç”¨çš„伺æœå™¨å¥—件,例如 httpdã€nginxã€mariadb"
+
+msgid "Choose which servers to install, if none then a minimal installation will be done"
+msgstr "é¸æ“‡è¦å®‰è£çš„顯示伺æœå™¨ï¼Œå¦‚果沒有,則進行最å°å®‰è£"
+
+msgid "Installs a minimal system as well as xorg and graphics drivers."
+msgstr "安è£æœ€å°ç³»çµ±ä»¥åŠ xorg 和圖形驅動程å¼ã€‚"
+
+msgid "Press Enter to continue."
+msgstr "按 Enter 以繼續。"
+
+msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?"
+msgstr "您想 chroot 進入新創建的安è£ä¸¦åŸ·è¡Œå®‰è£å¾Œçš„é…置嗎?"
+
+msgid "Are you sure you want to reset this setting?"
+msgstr "您確定è¦é‡ç½®æ­¤è¨­ç½®å—Žï¼Ÿ"
+
+msgid "Select one or more hard drives to use and configure\n"
+msgstr "é¸æ“‡ä¸€å€‹æˆ–多個硬碟æ¥ä½¿ç”¨å’Œé…ç½®\n"
+
+msgid "Any modifications to the existing setting will reset the disk layout!"
+msgstr "å°ç¾æœ‰è¨­ç½®çš„任何修改都將é‡ç½®ç¡¬ç¢Ÿä½ˆå±€ï¼"
+
+msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?"
+msgstr "如果é‡ç½®ç¡¬ç¢Ÿé¸æ“‡ï¼Œç•¶å‰ç¡¬ç¢Ÿä½ˆå±€ä¹Ÿå°‡è¢«é‡ç½®ã€‚ 您確定嗎?"
+
+msgid "Save and exit"
+msgstr "ä¿å­˜ä¸¦é€€å‡º"
+
+msgid ""
+"{}\n"
+"contains queued partitions, this will remove those, are you sure?"
+msgstr ""
+"{}\n"
+"包å«ä½‡åˆ—的分割å€ï¼Œé€™å°‡åˆªé™¤é€™äº›åˆ†å‰²å€ï¼Œæ‚¨ç¢ºå®šå—Žï¼Ÿ"
+
+msgid "No audio server"
+msgstr "沒有音訊伺æœå™¨"
+
+msgid "(default)"
+msgstr "(默èªï¼‰"
+
+msgid "Use ESC to skip"
+msgstr "按 ESC éµä»¥è·³éŽ"
+
+msgid ""
+"Use CTRL+C to reset current selection\n"
+"\n"
+msgstr "使用 CTRL+C å¯é‡ç½®ç•¶å‰é¸é …\n"
+
+msgid "Copy to: "
+msgstr "複製到:"
+
+msgid "Edit: "
+msgstr "編輯:"
+
+msgid "Key: "
+msgstr "密鑰:"
+
+msgid "Edit {}: "
+msgstr "編輯 {}: "
+
+msgid "Add: "
+msgstr "新增:"
+
+msgid "Value: "
+msgstr "值:"
+
+msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)"
+msgstr "您å¯ä»¥è·³éŽé¸æ“‡ç¡¬ç¢Ÿå’Œåˆ†å‰²å€ä¸¦ä½¿ç”¨ä»»ä¸€æŽ›è¼‰åœ¨ /mnt 的硬碟設置(實驗性)"
+
+msgid "Select one of the disks or skip and use /mnt as default"
+msgstr "é¸æ“‡ä¸€å€‹ç¡¬ç¢Ÿæˆ–è·³éŽä¸¦ä½¿ç”¨ /mnt 作為默èªå€¼"
+
+msgid "Select which partitions to mark for formatting:"
+msgstr "é¸æ“‡è¦æ¨™è¨˜ç‚ºæ ¼å¼åŒ–的分割å€ï¼š"
+
+msgid "Use HSM to unlock encrypted drive"
+msgstr "使用 HSM 解鎖加密硬碟"
+
+msgid "Device"
+msgstr "è£ç½®"
+
+msgid "Size"
+msgstr "大å°"
+
+msgid "Free space"
+msgstr "å¯ç”¨ç©ºé–“"
+
+msgid "Bus-type"
+msgstr "匯æµæŽ’é¡žåž‹"
+
+msgid "Either root-password or at least 1 user with sudo privileges must be specified"
+msgstr "必須指定 root 密碼或至少 1 個具有 sudo 權é™çš„使用者"
+
+msgid "Enter username (leave blank to skip): "
+msgstr "輸入用戶å(留空以跳éŽï¼‰ï¼š"
+
+msgid "The username you entered is invalid. Try again"
+msgstr "您輸入的用戶å無效。請é‡è©¦"
+
+msgid "Should \"{}\" be a superuser (sudo)?"
+msgstr "是å¦å°‡â€œ{}â€è¨­ç½®ç‚ºè¶…級使用者(sudo)?"
+
+msgid "Select which partitions to encrypt"
+msgstr "é¸æ“‡è¦åŠ å¯†çš„分割å€ï¼š"
+
+msgid "very weak"
+msgstr "éžå¸¸å¼±"
+
+msgid "weak"
+msgstr "å¼±"
+
+msgid "moderate"
+msgstr "一般"
+
+msgid "strong"
+msgstr "å¼·"
+
+msgid "Add subvolume"
+msgstr "新增å­å·"
+
+msgid "Edit subvolume"
+msgstr "編輯å­å·"
+
+msgid "Delete subvolume"
+msgstr "刪除å­å·"
+
+msgid "Configured {} interfaces"
+msgstr "å·²é…置的 {} 接å£"
+
+msgid "This option enables the number of parallel downloads that can occur during installation"
+msgstr "æ­¤é¸é …啟用安è£æœŸé–“å¯èƒ½ç™¼ç”Ÿçš„並行下載數"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+" (Enter a value between 1 to {})\n"
+"Note:"
+msgstr ""
+"輸入è¦å•Ÿç”¨çš„並行下載數。\n"
+" (輸入一個 1 到 {max_downloads} 之間的值)\n"
+"æ示:"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )"
+msgstr " - 最å°å€¼ï¼š1(å…許 1 个並行下載,åŒæ™‚å…許 2 个下載)"
+
+msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )"
+msgstr " - 最å°å€¼ï¼š1(å…許 1 个並行下載,åŒæ™‚å…許 2 个下載)"
+
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )"
+msgstr " - ç¦ç”¨/默èªï¼š0(ç¦ç”¨ä¸¦è¡Œä¸‹è¼‰ï¼ŒåŒæ™‚åªå…許 1 个下載)"
+
+#, python-brace-format
+msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]"
+msgstr "è¼¸å…¥ç„¡æ•ˆï¼ è«‹å˜—è©¦ä½¿ç”¨æœ‰æ•ˆè¼¸å…¥é‡è©¦ [1 到 {max_downloads},或 0 到ç¦ç”¨]"
+
+msgid "Parallel Downloads"
+msgstr "並行下載"
+
+msgid "ESC to skip"
+msgstr "按 ESC éµä»¥è·³éŽ"
+
+msgid "CTRL+C to reset"
+msgstr "按 CTRL+C 以é‡ç½®"
+
+msgid "TAB to select"
+msgstr "按 TAB 以é¸æ“‡"
+
+msgid "[Default value: 0] > "
+msgstr "[默èªå€¼: 0] > "
+
+msgid "To be able to use this translation, please install a font manually that supports the language."
+msgstr "為了能夠使用此翻譯,請手動安è£æ”¯æŒè©²èªžè¨€çš„字體。"
+
+msgid "The font should be stored as {}"
+msgstr "字體應儲存為 {}"
+
+msgid "Archinstall requires root privileges to run. See --help for more."
+msgstr "Archinstall éœ€è¦ root 權é™æ‰èƒ½é‹è¡Œã€‚有關更多信æ¯ï¼Œè«‹åƒé–± --help。"
+
+msgid "Select an execution mode"
+msgstr "é¸æ“‡åŸ·è¡Œæ¨¡å¼"
+
+msgid "Unable to fetch profile from specified url: {}"
+msgstr "無法從指定的 URL ç²å–é…置文件:{}"
+
+msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}"
+msgstr "é…置文件必須具有唯一的å稱,但找到具有é‡æ–°å稱的é…置文件定義:{}"
+
+msgid "Select one or more devices to use and configure"
+msgstr "é¸æ“‡ä¸€å€‹æˆ–多個硬碟æ¥ä½¿ç”¨å’Œé…ç½®"
+
+msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?"
+msgstr "如果é‡ç½®ç¡¬ç¢Ÿé¸æ“‡ï¼Œç•¶å‰ç¡¬ç¢Ÿä½ˆå±€ä¹Ÿå°‡è¢«é‡ç½®ã€‚ 您確定嗎?"
+
+msgid "Existing Partitions"
+msgstr "新增分割å€...."
+
+msgid "Select a partitioning option"
+msgstr "刪除一個分割å€"
+
+msgid "Enter the root directory of the mounted devices: "
+msgstr "輸入掛載è£ç½®çš„根目錄:"
+
+msgid "Minimum capacity for /home partition: {}GiB\n"
+msgstr "/home 分割å€çš„最å°å®¹é‡ç‚ºï¼š{}GB\n"
+
+msgid "Minimum capacity for Arch Linux partition: {}GiB"
+msgstr "Arch Linux 分割å€çš„最å°å®¹é‡ç‚ºï¼š{}GB"
+
+msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments"
+msgstr "以下是é ç·¨ç¨‹è¨­ç½®æª”案的列表,它們å¯ä»¥ä½¿å®‰è£æ¡Œé¢ç’°å¢ƒç­‰æ›´åŠ å®¹æ˜“"
+
+msgid "Current profile selection"
+msgstr "當å‰åˆ†å‰²å€ä½ˆå±€"
+
+msgid "Remove all newly added partitions"
+msgstr "創建新分割å€"
+
+msgid "Assign mountpoint"
+msgstr "分é…掛載點"
+
+msgid "Mark/Unmark to be formatted (wipes data)"
+msgstr "標記/å–消標記è¦æ ¼å¼åŒ–的分割å€ï¼ˆç§»é™¤æ•¸æ“šï¼‰"
+
+msgid "Mark/Unmark as bootable"
+msgstr "標記/å–消標記分割å€ç‚ºå¯å¼•å°Ž"
+
+msgid "Change filesystem"
+msgstr "更改文件系統"
+
+msgid "Mark/Unmark as compressed"
+msgstr "將分割å€æ¨™è¨˜/å–消標記為壓縮"
+
+msgid "Set subvolumes"
+msgstr "刪除å­å·"
+
+msgid "Delete partition"
+msgstr "刪除一個分割å€"
+
+msgid "Partition"
+msgstr "分割å€"
+
+msgid "This partition is currently encrypted, to format it a filesystem has to be specified"
+msgstr "此分å€ç•¶å‰å·²è¢«åŠ å¯†ï¼Œè¦å°‡å…¶æ ¼å¼åŒ–必須先指定文件系統"
+
+msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example."
+msgstr " * 分割å€æŽ›è¼‰é»žæ˜¯ç›¸å°æ–¼å®‰è£å†…部的,例如 boot 應該為 /boot。"
+
+msgid "If mountpoint /boot is set, then the partition will also be marked as bootable."
+msgstr "如果設置了掛載點/boot,則該分å€ä¹Ÿå°‡è¢«æ¨™è¨˜ç‚ºå¯å¼•å°Žã€‚"
+
+msgid "Mountpoint: "
+msgstr "掛載點:"
+
+msgid "Current free sectors on device {}:"
+msgstr "ç›®å‰ {} 設備上的å¯ç”¨å€å¡Šï¼š"
+
+msgid "Total sectors: {}"
+msgstr "總å€å¡Šæ•¸ï¼š{}"
+
+msgid "Enter the start sector (default: {}): "
+msgstr "輸入起始å€å¡Šï¼ˆé»˜èªï¼š{}):"
+
+msgid "Enter the end sector of the partition (percentage or block number, default: {}): "
+msgstr "輸入分割å€çš„结æŸå€å¡Šï¼ˆä»¥ç™¾åˆ†æ¯”或å€å¡Šç·¨è™Ÿè¡¨ç¤ºï¼Œé»˜èªï¼š{}):"
+
+msgid "This will remove all newly added partitions, continue?"
+msgstr "這將刪除所有新增的分割å€ï¼Œæ˜¯å¦ç¹¼çºŒï¼Ÿ"
+
+msgid "Partition management: {}"
+msgstr "分割å€ç®¡ç†ï¼š{}"
+
+msgid "Total length: {}"
+msgstr "總長度:{}"
+
+msgid "Encryption type"
+msgstr "加密類型"
+
+msgid "Partitions"
+msgstr "分割å€"
+
+msgid "No HSM devices available"
+msgstr "沒有å¯ç”¨çš„ HSM 設備"
+
+msgid "Partitions to be encrypted"
+msgstr "è¦åŠ å¯†çš„分割å€ï¼š"
+
+msgid "Select disk encryption option"
+msgstr "é¸æ“‡ç¡¬ç›¤åŠ å¯†é¸é …"
+
+msgid "Select a FIDO2 device to use for HSM"
+msgstr "é¸æ“‡è¦ç”¨æ–¼ HSM çš„ FIDO2 設備"
+
+msgid "Use a best-effort default partition layout"
+msgstr "使用最佳的é è¨­åˆ†å‰²ä½ˆå±€"
+
+msgid "Manual Partitioning"
+msgstr "手動分割"
+
+msgid "Pre-mounted configuration"
+msgstr "é æŽ›è¼‰é…ç½®"
+
+msgid "Unknown"
+msgstr "未知"
+
+msgid "Partition encryption"
+msgstr "分割å€åŠ å¯†"
+
+msgid " ! Formatting {} in "
+msgstr " ! 正在格å¼åŒ– {} 為 "
+
+msgid "↠Back"
+msgstr "↠返回"
+
+msgid "Disk encryption"
+msgstr "硬碟加密"
+
+msgid "Configuration"
+msgstr "é…ç½®"
+
+msgid "Password"
+msgstr "密碼"
+
+msgid "All settings will be reset, are you sure?"
+msgstr "所有設置將被é‡æ–°è¨­ç½®ï¼Œä½ ç¢ºå®šå—Žï¼Ÿ"
+
+msgid "Back"
+msgstr "返回"
+
+msgid "Please chose which greeter to install for the chosen profiles: {}"
+msgstr "è«‹é¸æ“‡ä¾æ“šé…置文件安è£çš„登錄管ç†å™¨ï¼š{}"
+
+msgid "Environment type: {}"
+msgstr "環境類型:{}"
+
+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 "Sway ä¸æ”¯æŒ Nvidia 的官方驅動。您å¯èƒ½æœƒé‡åˆ°ä¸€äº›å•é¡Œï¼Œæ‚¨ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ"
+
+msgid "Installed packages"
+msgstr "已安è£çš„套件"
+
+msgid "Add profile"
+msgstr "新增é…置文件"
+
+msgid "Edit profile"
+msgstr "編輯é…置文件"
+
+msgid "Delete profile"
+msgstr "刪除é…置文件"
+
+msgid "Profile name: "
+msgstr "é…置文件å稱:"
+
+msgid "The profile name you entered is already in use. Try again"
+msgstr "您輸入的é…置文件å稱已被使用。請é‡è©¦"
+
+msgid "Packages to be install with this profile (space separated, leave blank to skip): "
+msgstr "編寫è¦å®‰è£çš„套件(以空格分隔,留空以跳éŽï¼‰ï¼š"
+
+msgid "Services to be enabled with this profile (space separated, leave blank to skip): "
+msgstr "編寫è¦å•Ÿç”¨çš„附加æœå‹™ï¼ˆä»¥ç©ºæ ¼åˆ†éš”,留空以跳éŽï¼‰ï¼š"
+
+msgid "Should this profile be enabled for installation?"
+msgstr "是å¦å•Ÿç”¨æ­¤é…置文件進行安è£ï¼Ÿ"
+
+msgid "Create your own"
+msgstr "創建自己的"
+
+msgid ""
+"\n"
+"Select a graphics driver or leave blank to install all open-source drivers"
+msgstr ""
+"\n"
+"\n"
+"é¸æ“‡åœ–形驅動程å¼æˆ–留空以安è£é–‹æºé©…動程å¼"
+
+msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway 需è¦è¨ªå•æ‚¨çš„用戶環境(硬體設備的集åˆï¼Œä¾‹å¦‚éµç›¤ï¼Œæ»‘鼠等)"
+
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Sway access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"é¸æ“‡ä¸€å€‹é¸é …以æä¾› Sway å°æ‚¨ç¡¬ä»¶çš„訪å•æ¬Šé™"
+
+msgid "Graphics driver"
+msgstr "圖形驅動程å¼"
+
+msgid "Greeter"
+msgstr "登錄管ç†å™¨"
+
+msgid "Please chose which greeter to install"
+msgstr "è«‹é¸æ“‡è¦å®‰è£çš„登錄管ç†å™¨"
+
+msgid "This is a list of pre-programmed default_profiles"
+msgstr "這是é è¨­çš„默èªé…置文件列表"
+
+msgid "Disk configuration"
+msgstr "硬碟é…ç½®"
+
+msgid "Profiles"
+msgstr "é…置文件"
+
+msgid "Finding possible directories to save configuration files ..."
+msgstr "正在查找å¯èƒ½ç”¨æ–¼ä¿å­˜é…置文件的目錄 ..."
+
+msgid "Select directory (or directories) for saving configuration files"
+msgstr "é¸æ“‡ç”¨æ–¼ä¿å­˜é…置文件的目錄(或多個目錄)"
+
+#, fuzzy
+msgid "Add a custom mirror"
+msgstr "新增一個使用者"
+
+msgid "Change custom mirror"
+msgstr ""
+
+msgid "Delete custom mirror"
+msgstr ""
+
+#, fuzzy
+msgid "Enter name (leave blank to skip): "
+msgstr "輸入用戶å(留空以跳éŽï¼‰ï¼š"
+
+#, fuzzy
+msgid "Enter url (leave blank to skip): "
+msgstr "輸入用戶å(留空以跳éŽï¼‰ï¼š"
+
+#, fuzzy
+msgid "Select signature check option"
+msgstr "é¸æ“‡ç¡¬ç›¤åŠ å¯†é¸é …"
+
+#, fuzzy
+msgid "Select signature option"
+msgstr "é¸æ“‡ç¡¬ç›¤åŠ å¯†é¸é …"
+
+msgid "Custom mirrors"
+msgstr ""
+
+msgid "Defined"
+msgstr ""
+
+#, fuzzy
+msgid "Save user configuration (including disk layout)"
+msgstr "ä¿å­˜ä½¿ç”¨è€…é…ç½®"
+
+#, fuzzy
+msgid ""
+"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n"
+"Save directory: "
+msgstr "輸入è¦ä¿å­˜é…置的目錄:"
+
+msgid ""
+"Do you want to save {} configuration file(s) in the following location?\n"
+"\n"
+"{}"
+msgstr ""
+
+#, fuzzy
+msgid "Saving {} configuration files to {}"
+msgstr "ä¿å­˜é…ç½®"
+
+#, fuzzy
+msgid "Mirrors"
+msgstr "é¡åƒå€åŸŸ"
+
+#, fuzzy
+msgid "Mirror regions"
+msgstr "é¡åƒå€åŸŸ"
+
+#, fuzzy
+msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )"
+msgstr " - 最大值:{max_downloads}(å…許 {max_downloads} 个並行下載,åŒæ™‚å…許 {max_downloads+1} 个下載)"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]"
+msgstr "è¼¸å…¥ç„¡æ•ˆï¼ è«‹å˜—è©¦ä½¿ç”¨æœ‰æ•ˆè¼¸å…¥é‡è©¦ [1 到 {max_downloads},或 0 到ç¦ç”¨]"
+
+msgid "Locales"
+msgstr ""
+
+#, fuzzy
+msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
+msgstr "使用 NetworkManager(在 GNOME å’Œ KDE é€éŽåœ–形界é¢è¨­ç½®ç¶²éš›ç¶²è·¯é€£ç·šæ‰€éœ€ï¼‰"
+
+#, fuzzy
+msgid "Total: {} / {}"
+msgstr "總長度:{}"
+
+msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..."
+msgstr ""
+
+msgid "If no unit is provided, the value is interpreted as sectors"
+msgstr ""
+
+#, fuzzy
+msgid "Enter start (default: sector {}): "
+msgstr "輸入起始å€å¡Šï¼ˆé»˜èªï¼š{}):"
+
+#, fuzzy
+msgid "Enter end (default: {}): "
+msgstr "輸入起始å€å¡Šï¼ˆé»˜èªï¼š{}):"
+
+msgid "Unable to determine fido2 devices. Is libfido2 installed?"
+msgstr ""
+
+msgid "Path"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Product"
+msgstr ""
+
+#, fuzzy, python-brace-format
+msgid "Invalid configuration: {error}"
+msgstr "手動é…ç½®"
+
+msgid "Type"
+msgstr ""
+
+#, fuzzy
+msgid "This option enables the number of parallel downloads that can occur during package downloads"
+msgstr "æ­¤é¸é …啟用安è£æœŸé–“å¯èƒ½ç™¼ç”Ÿçš„並行下載數"
+
+#, fuzzy
+msgid ""
+"Enter the number of parallel downloads to be enabled.\n"
+"\n"
+"Note:\n"
+msgstr ""
+"輸入è¦å•Ÿç”¨çš„並行下載數。\n"
+" (輸入一個 1 到 {max_downloads} 之間的值)\n"
+"æ示:"
+
+#, fuzzy
+msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )"
+msgstr " - 最å°å€¼ï¼š1(å…許 1 个並行下載,åŒæ™‚å…許 2 个下載)"
+
+#, fuzzy
+msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n"
+msgstr " - ç¦ç”¨/默èªï¼š0(ç¦ç”¨ä¸¦è¡Œä¸‹è¼‰ï¼ŒåŒæ™‚åªå…許 1 个下載)"
+
+#, fuzzy
+msgid "Invalid input! Try again with a valid input [or 0 to disable]"
+msgstr "è¼¸å…¥ç„¡æ•ˆï¼ è«‹å˜—è©¦ä½¿ç”¨æœ‰æ•ˆè¼¸å…¥é‡è©¦ [1 到 {max_downloads},或 0 到ç¦ç”¨]"
+
+#, fuzzy
+msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
+msgstr "Sway 需è¦è¨ªå•æ‚¨çš„用戶環境(硬體設備的集åˆï¼Œä¾‹å¦‚éµç›¤ï¼Œæ»‘鼠等)"
+
+#, fuzzy
+msgid ""
+"\n"
+"\n"
+"Choose an option to give Hyprland access to your hardware"
+msgstr ""
+"\n"
+"\n"
+"é¸æ“‡ä¸€å€‹é¸é …以æä¾› Sway å°æ‚¨ç¡¬ä»¶çš„訪å•æ¬Šé™"
+
+msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..."
+msgstr ""
+
+#, fuzzy
+msgid "Would you like to use unified kernel images?"
+msgstr "您想在 zram 上使用交æ›åˆ†å‰²å€ï¼ˆswap)嗎?"
+
+msgid "Unified kernel images"
+msgstr ""
+
+msgid "Waiting for time sync (timedatectl show) to complete."
+msgstr ""
+
+msgid "Time syncronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"
+msgstr ""
+
+msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."
+msgstr ""
+
+#, fuzzy
+msgid "Selected profiles: "
+msgstr "刪除é…置文件"
+
+msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"
+msgstr ""
+
+#, fuzzy
+msgid "Mark/Unmark as nodatacow"
+msgstr "標記/å–消標記分割å€ç‚ºå¯å¼•å°Ž"
+
+#, fuzzy
+msgid "Would you like to use compression or disable CoW?"
+msgstr "您想使用 BTRFS 壓縮嗎?"
+
+msgid "Use compression"
+msgstr ""
+
+msgid "Disable Copy-on-Write"
+msgstr ""
diff --git a/archinstall/profiles b/archinstall/profiles
deleted file mode 120000
index c2968eea..00000000
--- a/archinstall/profiles
+++ /dev/null
@@ -1 +0,0 @@
-../profiles/ \ No newline at end of file
diff --git a/archinstall/scripts/__init__.py b/archinstall/scripts/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/archinstall/scripts/__init__.py
diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py
new file mode 100644
index 00000000..385ff500
--- /dev/null
+++ b/archinstall/scripts/guided.py
@@ -0,0 +1,247 @@
+from pathlib import Path
+from typing import Any, TYPE_CHECKING, Optional
+
+import archinstall
+from archinstall import info, debug
+from archinstall import SysInfo
+from archinstall.lib import locale
+from archinstall.lib import disk
+from archinstall.lib.global_menu import GlobalMenu
+from archinstall.lib.configuration import ConfigurationOutput
+from archinstall.lib.installer import Installer
+from archinstall.lib.menu import Menu
+from archinstall.lib.models import AudioConfiguration
+from archinstall.lib.models.bootloader import Bootloader
+from archinstall.lib.models.network_configuration import NetworkConfiguration
+from archinstall.lib.profile.profiles_handler import profile_handler
+
+if TYPE_CHECKING:
+ _: Any
+
+
+if archinstall.arguments.get('help'):
+ print("See `man archinstall` for help.")
+ exit(0)
+
+
+def ask_user_questions():
+ """
+ First, we'll ask the user for a bunch of user input.
+ Not until we're satisfied with what we want to install
+ will we continue with the actual installation steps.
+ """
+
+ # ref: https://github.com/archlinux/archinstall/pull/831
+ # we'll set NTP to true by default since this is also
+ # the default value specified in the menu options; in
+ # case it will be changed by the user we'll also update
+ # the system immediately
+ global_menu = GlobalMenu(data_store=archinstall.arguments)
+
+ global_menu.enable('archinstall-language')
+
+ # Set which region to download packages from during the installation
+ global_menu.enable('mirror_config')
+
+ global_menu.enable('locale_config')
+
+ global_menu.enable('disk_config', mandatory=True)
+
+ # Specify disk encryption options
+ global_menu.enable('disk_encryption')
+
+ # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB)
+ global_menu.enable('bootloader')
+
+ global_menu.enable('uki')
+
+ global_menu.enable('swap')
+
+ # Get the hostname for the machine
+ global_menu.enable('hostname')
+
+ # Ask for a root password (optional, but triggers requirement for super-user if skipped)
+ global_menu.enable('!root-password', mandatory=True)
+
+ global_menu.enable('!users', mandatory=True)
+
+ # Ask for archinstall-specific profiles_bck (such as desktop environments etc)
+ global_menu.enable('profile_config')
+
+ # Ask about audio server selection if one is not already set
+ global_menu.enable('audio_config')
+
+ # Ask for preferred kernel:
+ global_menu.enable('kernels', mandatory=True)
+
+ global_menu.enable('packages')
+
+ if archinstall.arguments.get('advanced', False):
+ # Enable parallel downloads
+ global_menu.enable('parallel downloads')
+
+ # Ask or Call the helper function that asks the user to optionally configure a network.
+ global_menu.enable('network_config')
+
+ global_menu.enable('timezone')
+
+ global_menu.enable('ntp')
+
+ global_menu.enable('additional-repositories')
+
+ global_menu.enable('__separator__')
+
+ global_menu.enable('save_config')
+ global_menu.enable('install')
+ global_menu.enable('abort')
+
+ global_menu.run()
+
+
+def perform_installation(mountpoint: Path):
+ """
+ Performs the installation steps on a block device.
+ Only requirement is that the block devices are
+ formatted and setup prior to entering this function.
+ """
+ info('Starting installation...')
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+
+ # Retrieve list of additional repositories and set boolean values appropriately
+ enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', [])
+ enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', [])
+ run_mkinitcpio = not archinstall.arguments.get('uki')
+ locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ # Mount all the drives to the desired mountpoint
+ if disk_config.config_type != disk.DiskLayoutType.Pre_mount:
+ installation.mount_ordered_layout()
+
+ installation.sanity_check()
+
+ if disk_config.config_type != disk.DiskLayoutType.Pre_mount:
+ if disk_encryption and disk_encryption.encryption_type != disk.EncryptionType.NoEncryption:
+ # generate encryption key files for the mounted luks devices
+ installation.generate_key_files()
+
+ if mirror_config := archinstall.arguments.get('mirror_config', None):
+ installation.set_mirrors(mirror_config, on_target=False)
+
+ installation.minimal_installation(
+ testing=enable_testing,
+ multilib=enable_multilib,
+ mkinitcpio=run_mkinitcpio,
+ hostname=archinstall.arguments.get('hostname', 'archlinux'),
+ locale_config=locale_config
+ )
+
+ if mirror_config := archinstall.arguments.get('mirror_config', None):
+ installation.set_mirrors(mirror_config, on_target=True)
+
+ if archinstall.arguments.get('swap'):
+ installation.setup_swap('zram')
+
+ if archinstall.arguments.get("bootloader") == Bootloader.Grub and SysInfo.has_uefi():
+ installation.add_additional_packages("grub")
+
+ installation.add_bootloader(
+ archinstall.arguments["bootloader"],
+ archinstall.arguments.get('uki', False)
+ )
+
+ # If user selected to copy the current ISO network configuration
+ # Perform a copy of the config
+ network_config: Optional[NetworkConfiguration] = archinstall.arguments.get('network_config', None)
+
+ if network_config:
+ network_config.install_network_config(
+ installation,
+ archinstall.arguments.get('profile_config', None)
+ )
+
+ if users := archinstall.arguments.get('!users', None):
+ installation.create_users(users)
+
+ audio_config: Optional[AudioConfiguration] = archinstall.arguments.get('audio_config', None)
+ if audio_config:
+ audio_config.install_audio_config(installation)
+ else:
+ info("No audio server will be installed")
+
+ if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '':
+ installation.add_additional_packages(archinstall.arguments.get('packages', None))
+
+ if profile_config := archinstall.arguments.get('profile_config', None):
+ profile_handler.install_profile_config(installation, profile_config)
+
+ if timezone := archinstall.arguments.get('timezone', None):
+ installation.set_timezone(timezone)
+
+ if archinstall.arguments.get('ntp', False):
+ installation.activate_time_synchronization()
+
+ if archinstall.accessibility_tools_in_use():
+ installation.enable_espeakup()
+
+ if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
+ installation.user_set_pw('root', root_pw)
+
+ if profile_config := archinstall.arguments.get('profile_config', None):
+ profile_config.profile.post_install(installation)
+
+ # If the user provided a list of services to be enabled, pass the list to the enable_service function.
+ # Note that while it's called enable_service, it can actually take a list of services and iterate it.
+ if archinstall.arguments.get('services', None):
+ installation.enable_service(archinstall.arguments.get('services', []))
+
+ # If the user provided custom commands to be run post-installation, execute them now.
+ if archinstall.arguments.get('custom-commands', None):
+ archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation)
+
+ installation.genfstab()
+
+ info("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation")
+
+ if not archinstall.arguments.get('silent'):
+ prompt = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?'))
+ choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
+ if choice.value == Menu.yes():
+ try:
+ installation.drop_to_shell()
+ except:
+ pass
+
+ debug(f"Disk states after installing: {disk.disk_layouts()}")
+
+
+if not archinstall.arguments.get('silent'):
+ ask_user_questions()
+
+config_output = ConfigurationOutput(archinstall.arguments)
+
+if not archinstall.arguments.get('silent'):
+ config_output.show()
+
+config_output.save()
+
+if archinstall.arguments.get('dry_run'):
+ exit(0)
+
+if not archinstall.arguments.get('silent'):
+ input(str(_('Press Enter to continue.')))
+
+fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+)
+
+fs_handler.perform_filesystem_operations()
+
+perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')))
diff --git a/archinstall/scripts/list.py b/archinstall/scripts/list.py
new file mode 100644
index 00000000..0e0363a1
--- /dev/null
+++ b/archinstall/scripts/list.py
@@ -0,0 +1,10 @@
+import glob
+import pathlib
+
+print("The following are viable --script options:")
+
+for script in [pathlib.Path(x) for x in glob.glob(f"{pathlib.Path(__file__).parent}/*.py")]:
+ if script.stem in ['__init__', 'list']:
+ continue
+
+ print(f" {script.stem}") \ No newline at end of file
diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py
new file mode 100644
index 00000000..f6650ff8
--- /dev/null
+++ b/archinstall/scripts/minimal.py
@@ -0,0 +1,106 @@
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, List
+
+import archinstall
+from archinstall import info
+from archinstall import Installer, ConfigurationOutput
+from archinstall.default_profiles.minimal import MinimalProfile
+from archinstall.lib.interactions import suggest_single_disk_layout, select_devices
+from archinstall.lib.models import Bootloader, User
+from archinstall.lib.profile import ProfileConfiguration, profile_handler
+from archinstall.lib import disk
+
+if TYPE_CHECKING:
+ _: Any
+
+
+info("Minimal only supports:")
+info(" * Being installed to a single disk")
+
+if archinstall.arguments.get('help', None):
+ info(" - Optional disk encryption via --!encryption-password=<password>")
+ info(" - Optional filesystem type via --filesystem=<fs type>")
+ info(" - Optional systemd network via --network")
+
+
+def perform_installation(mountpoint: Path):
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ # Strap in the base system, add a boot loader and configure
+ # some other minor details as specified by this profile and user.
+ if installation.minimal_installation():
+ installation.set_hostname('minimal-arch')
+ installation.add_bootloader(Bootloader.Systemd)
+
+ # Optionally enable networking:
+ if archinstall.arguments.get('network', None):
+ installation.copy_iso_network_config(enable_services=True)
+
+ installation.add_additional_packages(['nano', 'wget', 'git'])
+
+ profile_config = ProfileConfiguration(MinimalProfile())
+ profile_handler.install_profile_config(installation, profile_config)
+
+ user = User('devel', 'devel', False)
+ installation.create_users(user)
+
+ # Once this is done, we output some useful information to the user
+ # And the installation is complete.
+ info("There are two new accounts in your installation after reboot:")
+ info(" * root (password: airoot)")
+ info(" * devel (password: devel)")
+
+
+def prompt_disk_layout():
+ fs_type = None
+ if filesystem := archinstall.arguments.get('filesystem', None):
+ fs_type = disk.FilesystemType(filesystem)
+
+ devices = select_devices()
+ modifications = suggest_single_disk_layout(devices[0], filesystem_type=fs_type)
+
+ archinstall.arguments['disk_config'] = disk.DiskLayoutConfiguration(
+ config_type=disk.DiskLayoutType.Default,
+ device_modifications=[modifications]
+ )
+
+
+def parse_disk_encryption():
+ if enc_password := archinstall.arguments.get('!encryption-password', None):
+ modification: List[disk.DeviceModification] = archinstall.arguments['disk_config']
+ partitions: List[disk.PartitionModification] = []
+
+ # encrypt all partitions except the /boot
+ for mod in modification:
+ partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions))
+
+ archinstall.arguments['disk_encryption'] = disk.DiskEncryption(
+ encryption_type=disk.EncryptionType.Luks,
+ encryption_password=enc_password,
+ partitions=partitions
+ )
+
+
+prompt_disk_layout()
+parse_disk_encryption()
+
+config_output = ConfigurationOutput(archinstall.arguments)
+config_output.show()
+
+input(str(_('Press Enter to continue.')))
+
+fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+)
+
+fs_handler.perform_filesystem_operations()
+
+perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')))
diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py
new file mode 100644
index 00000000..2293cbee
--- /dev/null
+++ b/archinstall/scripts/only_hd.py
@@ -0,0 +1,78 @@
+from pathlib import Path
+
+import archinstall
+from archinstall import debug
+from archinstall.lib.installer import Installer
+from archinstall.lib.configuration import ConfigurationOutput
+from archinstall.lib import disk
+
+
+def ask_user_questions():
+ global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments)
+
+ global_menu.enable('archinstall-language')
+
+ global_menu.enable('disk_config', mandatory=True)
+ global_menu.enable('disk_encryption')
+ global_menu.enable('swap')
+
+ global_menu.enable('save_config')
+ global_menu.enable('install')
+ global_menu.enable('abort')
+
+ global_menu.run()
+
+
+def perform_installation(mountpoint: Path):
+ """
+ Performs the installation steps on a block device.
+ Only requirement is that the block devices are
+ formatted and setup prior to entering this function.
+ """
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ # Mount all the drives to the desired mountpoint
+ # This *can* be done outside of the installation, but the installer can deal with it.
+ if archinstall.arguments.get('disk_config'):
+ installation.mount_ordered_layout()
+
+ # to generate a fstab directory holder. Avoids an error on exit and at the same time checks the procedure
+ target = Path(f"{mountpoint}/etc/fstab")
+ if not target.parent.exists():
+ target.parent.mkdir(parents=True)
+
+ # For support reasons, we'll log the disk layout post installation (crash or no crash)
+ debug(f"Disk states after installing: {disk.disk_layouts()}")
+
+
+if not archinstall.arguments.get('silent'):
+ ask_user_questions()
+
+config_output = ConfigurationOutput(archinstall.arguments)
+if not archinstall.arguments.get('silent'):
+ config_output.show()
+
+config_output.save()
+
+if archinstall.arguments.get('dry_run'):
+ exit(0)
+
+if not archinstall.arguments.get('silent'):
+ input('Press Enter to continue.')
+
+fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+)
+
+fs_handler.perform_filesystem_operations()
+
+
+perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')))
diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py
new file mode 100644
index 00000000..8813fd91
--- /dev/null
+++ b/archinstall/scripts/swiss.py
@@ -0,0 +1,308 @@
+from enum import Enum
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Dict, Optional
+
+import archinstall
+from archinstall import SysInfo, info, debug
+from archinstall.lib import models
+from archinstall.lib import disk
+from archinstall.lib import locale
+from archinstall.lib.models import AudioConfiguration
+from archinstall.lib.profile.profiles_handler import profile_handler
+from archinstall.lib import menu
+from archinstall.lib.global_menu import GlobalMenu
+from archinstall.lib.installer import Installer
+from archinstall.lib.configuration import ConfigurationOutput
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class ExecutionMode(Enum):
+ Full = 'full'
+ Lineal = 'lineal'
+ Only_HD = 'only-hd'
+ Only_OS = 'only-os'
+ Minimal = 'minimal'
+
+
+def select_mode() -> ExecutionMode:
+ options = [str(e.value) for e in ExecutionMode]
+ choice = menu.Menu(
+ str(_('Select an execution mode')),
+ options,
+ default_option=ExecutionMode.Full.value,
+ skip=False
+ ).run()
+
+ return ExecutionMode(choice.single_value)
+
+
+class SetupMenu(GlobalMenu):
+ def __init__(self, storage_area: Dict[str, Any]):
+ super().__init__(data_store=storage_area)
+
+ def setup_selection_menu_options(self):
+ super().setup_selection_menu_options()
+
+ self._menu_options['mode'] = menu.Selector(
+ 'Execution mode',
+ lambda x : select_mode(),
+ display_func=lambda x: x.value if x else '',
+ default=ExecutionMode.Full)
+
+ self._menu_options['continue'] = menu.Selector(
+ 'Continue',
+ exec_func=lambda n,v: True)
+
+ self.enable('archinstall-language')
+ self.enable('ntp')
+ self.enable('mode')
+ self.enable('continue')
+ self.enable('abort')
+
+ def exit_callback(self):
+ if self._data_store.get('mode', None):
+ archinstall.arguments['mode'] = self._data_store['mode']
+ info(f"Archinstall will execute under {archinstall.arguments['mode']} mode")
+
+
+class SwissMainMenu(GlobalMenu):
+ def __init__(
+ self,
+ data_store: Dict[str, Any],
+ exec_mode: ExecutionMode = ExecutionMode.Full
+ ):
+ self._execution_mode = exec_mode
+ super().__init__(data_store)
+
+ def setup_selection_menu_options(self):
+ super().setup_selection_menu_options()
+
+ options_list = []
+ mandatory_list = []
+
+ match self._execution_mode:
+ case ExecutionMode.Full | ExecutionMode.Lineal:
+ options_list = [
+ 'mirror_config', 'disk_config',
+ 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password',
+ '!users', 'profile_config', 'audio_config', 'kernels', 'packages', 'additional-repositories', 'network_config',
+ 'timezone', 'ntp'
+ ]
+
+ if archinstall.arguments.get('advanced', False):
+ options_list.extend(['locale_config'])
+
+ mandatory_list = ['disk_config', 'bootloader', 'hostname']
+ case ExecutionMode.Only_HD:
+ options_list = ['disk_config', 'disk_encryption','swap']
+ mandatory_list = ['disk_config']
+ case ExecutionMode.Only_OS:
+ options_list = [
+ 'mirror_config','bootloader', 'hostname',
+ '!root-password', '!users', 'profile_config', 'audio_config', 'kernels',
+ 'packages', 'additional-repositories', 'network_config', 'timezone', 'ntp'
+ ]
+
+ mandatory_list = ['hostname']
+
+ if archinstall.arguments.get('advanced', False):
+ options_list += ['locale_config']
+ case ExecutionMode.Minimal:
+ pass
+ case _:
+ info(f' Execution mode {self._execution_mode} not supported')
+ exit(1)
+
+ if self._execution_mode != ExecutionMode.Lineal:
+ options_list += ['save_config', 'install']
+
+ if not archinstall.arguments.get('advanced', False):
+ options_list.append('archinstall-language')
+
+ options_list += ['abort']
+
+ for entry in mandatory_list:
+ self.enable(entry, mandatory=True)
+
+ for entry in options_list:
+ self.enable(entry)
+
+
+def ask_user_questions(exec_mode: ExecutionMode = ExecutionMode.Full):
+ """
+ First, we'll ask the user for a bunch of user input.
+ Not until we're satisfied with what we want to install
+ will we continue with the actual installation steps.
+ """
+ if archinstall.arguments.get('advanced', None):
+ setup_area: Dict[str, Any] = {}
+ setup = SetupMenu(setup_area)
+
+ if exec_mode == ExecutionMode.Lineal:
+ for entry in setup.list_enabled_options():
+ if entry in ('continue', 'abort'):
+ continue
+ if not setup.option(entry).enabled:
+ continue
+ setup.exec_option(entry)
+ else:
+ setup.run()
+
+ archinstall.arguments['archinstall-language'] = setup_area.get('archinstall-language')
+
+ with SwissMainMenu(data_store=archinstall.arguments, exec_mode=exec_mode) as menu:
+ if mode == ExecutionMode.Lineal:
+ for entry in menu.list_enabled_options():
+ if entry in ('install', 'abort'):
+ continue
+ menu.exec_option(entry)
+ archinstall.arguments[entry] = menu.option(entry).get_selection()
+ else:
+ menu.run()
+
+
+def perform_installation(mountpoint: Path, exec_mode: ExecutionMode):
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', [])
+ enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', [])
+ locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config']
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ if exec_mode in [ExecutionMode.Full, ExecutionMode.Only_HD]:
+ installation.mount_ordered_layout()
+
+ installation.sanity_check()
+
+ if disk_config.config_type != disk.DiskLayoutType.Pre_mount:
+ if disk_encryption and disk_encryption.encryption_type != disk.EncryptionType.NoEncryption:
+ # generate encryption key files for the mounted luks devices
+ installation.generate_key_files()
+
+ if mirror_config := archinstall.arguments.get('mirror_config', None):
+ installation.set_mirrors(mirror_config)
+
+ installation.minimal_installation(
+ testing=enable_testing,
+ multilib=enable_multilib,
+ hostname=archinstall.arguments.get('hostname', 'archlinux'),
+ locale_config=locale_config
+ )
+
+ if mirror_config := archinstall.arguments.get('mirror_config', None):
+ installation.set_mirrors(mirror_config, on_target=True)
+
+ if archinstall.arguments.get('swap'):
+ installation.setup_swap('zram')
+
+ if archinstall.arguments.get("bootloader") == models.Bootloader.Grub and SysInfo.has_uefi():
+ installation.add_additional_packages("grub")
+
+ installation.add_bootloader(archinstall.arguments["bootloader"])
+
+ # If user selected to copy the current ISO network configuration
+ # Perform a copy of the config
+ network_config = archinstall.arguments.get('network_config', None)
+
+ if network_config:
+ network_config.install_network_config(
+ installation,
+ archinstall.arguments.get('profile_config', None)
+ )
+
+ if users := archinstall.arguments.get('!users', None):
+ installation.create_users(users)
+
+ audio_config: Optional[AudioConfiguration] = archinstall.arguments.get('audio_config', None)
+ if audio_config:
+ audio_config.install_audio_config(installation)
+ else:
+ info("No audio server will be installed")
+
+ if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '':
+ installation.add_additional_packages(archinstall.arguments.get('packages', []))
+
+ if profile_config := archinstall.arguments.get('profile_config', None):
+ profile_handler.install_profile_config(installation, profile_config)
+
+ if timezone := archinstall.arguments.get('timezone', None):
+ installation.set_timezone(timezone)
+
+ if archinstall.arguments.get('ntp', False):
+ installation.activate_time_synchronization()
+
+ if archinstall.accessibility_tools_in_use():
+ installation.enable_espeakup()
+
+ if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
+ installation.user_set_pw('root', root_pw)
+
+ if profile_config := archinstall.arguments.get('profile_config', None):
+ profile_config.profile.post_install(installation)
+
+ # If the user provided a list of services to be enabled, pass the list to the enable_service function.
+ # Note that while it's called enable_service, it can actually take a list of services and iterate it.
+ if archinstall.arguments.get('services', None):
+ installation.enable_service(archinstall.arguments.get('services', []))
+
+ # If the user provided custom commands to be run post-installation, execute them now.
+ if archinstall.arguments.get('custom-commands', None):
+ archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation)
+
+ installation.genfstab()
+
+ info("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation")
+
+ if not archinstall.arguments.get('silent'):
+ prompt = str(
+ _('Would you like to chroot into the newly created installation and perform post-installation configuration?'))
+ choice = menu.Menu(prompt, menu.Menu.yes_no(), default_option=menu.Menu.yes()).run()
+ if choice.value == menu.Menu.yes():
+ try:
+ installation.drop_to_shell()
+ except:
+ pass
+
+ debug(f"Disk states after installing: {disk.disk_layouts()}")
+
+
+param_mode = archinstall.arguments.get('mode', ExecutionMode.Full.value).lower()
+
+try:
+ mode = ExecutionMode(param_mode)
+except KeyError:
+ info(f'Mode "{param_mode}" is not supported')
+ exit(1)
+
+if not archinstall.arguments.get('silent'):
+ ask_user_questions(mode)
+
+config_output = ConfigurationOutput(archinstall.arguments)
+if not archinstall.arguments.get('silent'):
+ config_output.show()
+
+config_output.save()
+
+if archinstall.arguments.get('dry_run'):
+ exit(0)
+
+if not archinstall.arguments.get('silent'):
+ input('Press Enter to continue.')
+
+if mode in (ExecutionMode.Full, ExecutionMode.Only_HD):
+ fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+ )
+
+ fs_handler.perform_filesystem_operations()
+
+perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')), mode)
diff --git a/archinstall/scripts/unattended.py b/archinstall/scripts/unattended.py
new file mode 100644
index 00000000..5ae4ae3d
--- /dev/null
+++ b/archinstall/scripts/unattended.py
@@ -0,0 +1,19 @@
+import time
+
+import archinstall
+from archinstall import info
+from archinstall import profile
+
+for p in profile.profile_handler.get_mac_addr_profiles():
+ # Tailored means it's a match for this machine
+ # based on it's MAC address (or some other criteria
+ # that fits the requirements for this machine specifically).
+ info(f'Found a tailored profile for this machine called: "{p.name}"')
+
+ print('Starting install in:')
+ for i in range(10, 0, -1):
+ print(f'{i}...')
+ time.sleep(1)
+
+ install_session = archinstall.storage['installation_session']
+ p.install(install_session)
diff --git a/build_iso.sh b/build_iso.sh
new file mode 100755
index 00000000..682b69df
--- /dev/null
+++ b/build_iso.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+set -e
+
+packages_file="/tmp/archlive/packages.x86_64"
+
+# Packages to add to the archiso profile packages
+packages=(
+ gcc
+ git
+ pkgconfig
+ python
+ python-pip
+ python-build
+ python-setuptools
+ python-wheel
+ python-simple-term-menu
+ python-pyparted
+)
+
+mkdir -p /tmp/archlive/airootfs/root/archinstall-git
+cp -r . /tmp/archlive/airootfs/root/archinstall-git
+
+cat <<- _EOF_ | tee /tmp/archlive/airootfs/root/.zprofile
+ cd archinstall-git
+ rm -rf dist
+
+ python -m build --wheel --no-isolation
+ pip install dist/archinstall*.whl --break-system-packages
+
+ echo "This is an unofficial ISO for development and testing of archinstall. No support will be provided."
+ echo "This ISO was built from Git SHA $GITHUB_SHA"
+ echo "Type archinstall to launch the installer."
+_EOF_
+
+pacman --noconfirm -S archiso
+
+cp -r /usr/share/archiso/configs/releng/* /tmp/archlive
+
+sed -i /archinstall/d "$packages_file"
+
+# Add packages to the archiso profile packages
+for package in "${packages[@]}"; do
+ echo "$package" >> "$packages_file"
+done
+
+find /tmp/archlive
+cd /tmp/archlive
+
+mkarchiso -v -w work/ -o out/ ./
diff --git a/docs/_static/style.css b/docs/_static/style.css
new file mode 100644
index 00000000..579fe077
--- /dev/null
+++ b/docs/_static/style.css
@@ -0,0 +1,3 @@
+.wy-nav-content {
+ max-width: none;
+} \ No newline at end of file
diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html
new file mode 100644
index 00000000..b0a44806
--- /dev/null
+++ b/docs/_templates/layout.html
@@ -0,0 +1,4 @@
+{% extends "!layout.html" %}
+{% block extrahead %}
+ <link href="{{ pathto("_static/style.css", True) }}" rel="stylesheet" type="text/css">
+{% endblock %} \ No newline at end of file
diff --git a/docs/archinstall/Application.rst b/docs/archinstall/Application.rst
deleted file mode 100644
index c6eb0d4d..00000000
--- a/docs/archinstall/Application.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-.. _archinstall.Application:
-
-archinstall.Application
-=======================
-
-This class enables access to pre-programmed application configurations.
-This is not to be confused with :ref:`archinstall.Profile` which is for pre-programmed profiles for a wider set of installation sets.
-
-
-.. autofunction:: archinstall.Application
diff --git a/docs/archinstall/Profile.rst b/docs/archinstall/Profile.rst
deleted file mode 100644
index a4c1f3bb..00000000
--- a/docs/archinstall/Profile.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. _archinstall.Profile:
-
-archinstall.Profile
-===================
-
-This class enables access to pre-programmed profiles.
-This is not to be confused with :ref:`archinstall.Application` which is for pre-programmed application profiles.
-
-Profiles in general is a set or group of installation steps.
-Where as applications are a specific set of instructions for a very specific application.
-
-An example would be the *(currently fictional)* profile called `database`.
-The profile `database` might contain the application profile `postgresql`.
-And that's the difference between :ref:`archinstall.Profile` and :ref:`archinstall.Application`.
-
-.. autofunction:: archinstall.Profile
diff --git a/docs/archinstall/general.rst b/docs/archinstall/general.rst
deleted file mode 100644
index 970d40f2..00000000
--- a/docs/archinstall/general.rst
+++ /dev/null
@@ -1,117 +0,0 @@
-.. _archinstall.helpers:
-
-.. warning::
- All these helper functions are mostly, if not all, related to outside-installation-instructions. Meaning the calls will affect your current running system - and not touch your installed system.
-
-Profile related helpers
-=======================
-
-.. autofunction:: archinstall.list_profiles
-
-Packages
-========
-
-.. autofunction:: archinstall.find_package
-
-.. autofunction:: archinstall.find_packages
-
-Locale related
-==============
-
-.. autofunction:: archinstall.list_keyboard_languages
-
-.. autofunction:: archinstall.search_keyboard_layout
-
-.. autofunction:: archinstall.set_keyboard_language
-
-..
- autofunction:: archinstall.Installer.set_keyboard_layout
-
-Services
-========
-
-.. autofunction:: archinstall.service_state
-
-Mirrors
-=======
-
-.. autofunction:: archinstall.filter_mirrors_by_region
-
-.. autofunction:: archinstall.add_custom_mirrors
-
-.. autofunction:: archinstall.insert_mirrors
-
-.. autofunction:: archinstall.use_mirrors
-
-.. autofunction:: archinstall.re_rank_mirrors
-
-.. autofunction:: archinstall.list_mirrors
-
-Disk related
-============
-
-.. autofunction:: archinstall.BlockDevice
-
-.. autofunction:: archinstall.Partition
-
-.. autofunction:: archinstall.Filesystem
-
-.. autofunction:: archinstall.device_state
-
-.. autofunction:: archinstall.all_blockdevices
-
-Luks (Disk encryption)
-======================
-
-.. autofunction:: archinstall.luks2
-
-Networking
-==========
-
-.. autofunction:: archinstall.get_hw_addr
-
-.. autofunction:: archinstall.list_interfaces
-
-.. autofunction:: archinstall.check_mirror_reachable
-
-.. autofunction:: archinstall.update_keyring
-
-.. autofunction:: archinstall.enrich_iface_types
-
-.. autofunction:: archinstall.get_interface_from_mac
-
-.. autofunction:: archinstall.wireless_scan
-
-.. autofunction:: archinstall.get_wireless_networks
-
-General
-=======
-
-.. autofunction:: archinstall.log
-
-.. autofunction:: archinstall.locate_binary
-
-.. autofunction:: archinstall.SysCommand
-
-.. autofunction:: archinstall.SysCommandWorker
-
-Exceptions
-==========
-
-.. autofunction:: archinstall.RequirementError
-
-.. autofunction:: archinstall.DiskError
-
-.. autofunction:: archinstall.ProfileError
-
-.. autofunction:: archinstall.SysCallError
-
-.. autofunction:: archinstall.ProfileNotFound
-
-.. autofunction:: archinstall.HardwareIncompatibilityError
-
-.. autofunction:: archinstall.PermissionError
-
-.. autofunction:: archinstall.UserError
-
-.. autofunction:: archinstall.ServiceException
diff --git a/docs/archinstall/plugins.rst b/docs/archinstall/plugins.rst
new file mode 100644
index 00000000..898f9006
--- /dev/null
+++ b/docs/archinstall/plugins.rst
@@ -0,0 +1,57 @@
+.. _archinstall.Plugins:
+
+Python Plugins
+==============
+
+``archinstall`` supports plugins via two methods.
+
+First method is directly via the ``--plugin`` parameter when running as a CLI tool. This will load a specific plugin locally or remotely via a path.
+
+The second method is via Python's built in `plugin discovery`_ using `entry points`_ categorized as ``archinstall.plugin``.
+
+``--plugin`` parameter
+----------------------
+
+The parameter has the benefit of being stored in the ``--conf`` state, meaning when re-running an installation — the plugin will automatically be loaded.
+It's limitation is that it requires an initial path to be known and written and be cumbersome.
+
+Plugin Discovery
+----------------
+
+This method allows for multiple plugins to be loaded with the drawback that they have to be installed beforehand on the system running ``archinstall``.
+This mainly targets those who build their own ISO's and package specific setups for their needs.
+
+
+What's supported?
+-----------------
+
+Currently the documentation for this is scarse. Until that is resolved, the best way to find supported features is to search the source code for `plugin.on_ <https://github.com/search?q=repo%3Aarchlinux%2Farchinstall+%22plugin.on_%22&type=code>`_ as this will give a clear indication of which calls are made to plugins.
+
+How does it work?
+-----------------
+
+``archinstall`` plugins use a discovery-driven approach where plugins are queried for certain functions.
+As an example, if a plugin has the following function:
+
+.. code-block:: python
+
+ def on_pacstrap(*packages):
+ ...
+
+The function :code:`archinstall.Pacman().strap(["some packages"])` is hardcoded to iterate plugins and look for :code:`on_pacstrap` in the plugin.
+If the function exists, :code:`.strap()` will call the plugin's function and replace the initial package list with the result from the plugin.
+
+The best way to document these calls is currently undecided, as it's hard to document this behavior dynamically.
+
+Writing your own?
+-----------------
+
+The simplest way currently is to look at a reference implementation or the community. Two of these are:
+
+* `torxed/archinstall-aur <https://github.com/torxed/archinstall-aur>`_
+* `phisch/archinstall-aur <https://github.com/phisch/archinstall-aur>`_
+
+And search for `plugin.on_ <https://github.com/search?q=repo%3Aarchlinux%2Farchinstall+%22plugin.on_%22&type=code>`_ in the code base to find what ``archinstall`` will look for. PR's are welcome to widen the support for this.
+
+.. _plugin discovery: https://packaging.python.org/en/latest/specifications/entry-points/
+.. _entry points: https://docs.python.org/3/library/importlib.metadata.html#entry-points \ No newline at end of file
diff --git a/docs/cli_parameters/config/config_options.csv b/docs/cli_parameters/config/config_options.csv
new file mode 100644
index 00000000..7cf10982
--- /dev/null
+++ b/docs/cli_parameters/config/config_options.csv
@@ -0,0 +1,24 @@
+Key,Value(s),Description,Required
+additional-repositories,[ `multilib <https://wiki.archlinux.org/title/Official_repositories#multilib>`_!, `testing <https://wiki.archlinux.org/title/Official_repositories#Testing_repositories>`_ ],Enables one or more of the testing and multilib repositories before proceeding with installation,No
+archinstall-language,`lang <https://github.com/archlinux/archinstall/blob/master/archinstall/locales/languages.json>`__,Sets the TUI language used *(make sure to use the ``lang`` value not the ``abbr``)*,No
+audio_config,`pipewire <https://wiki.archlinux.org/title/PipeWire>`_!, `pulseaudio <https://wiki.archlinux.org/title/PulseAudio>`_,Audioserver to be installed,No
+bootloader,`Systemd-boot <https://wiki.archlinux.org/title/Systemd-boot>`_!, `grub <https://wiki.archlinux.org/title/GRUB>`_,Bootloader to be installed *(grub being mandatory on BIOS machines)*,Yes
+debug,``true``!, ``false``,Enables debug output,No
+disk_config,*Read more under* :ref:`disk config`,Contains the desired disk setup to be used during installation,No
+disk_encryption,*Read more about under* :ref:`disk encryption`,Parameters for disk encryption applied on top of ``disk_config``,No
+hostname,``str``,A string defining your machines hostname on the network *(defaults to ``archinstall``)*,No
+kernels,[ `linux <https://wiki.archlinux.org/title/Kernel#Officially_supported_kernels>`_!, `linux-hardened <https://wiki.archlinux.org/title/Kernel#Officially_supported_kernels>`_!, `linux-lts <https://wiki.archlinux.org/title/Kernel#Officially_supported_kernels>`_!, `linux-rt <https://wiki.archlinux.org/title/Kernel#Officially_supported_kernels>`_!, `linux-rt-lts <https://wiki.archlinux.org/title/Kernel#Officially_supported_kernels>`_!, `linux-zen <https://wiki.archlinux.org/title/Kernel#Officially_supported_kernels>`_ ],Defines which kernels should be installed and setup in the boot loader options,Yes
+custom-commands,*Read more under* :ref:`custom commands`,Custom commands that will be run post-install chrooted inside the installed system,No
+locale_config,{kb_layout: `lang <https://wiki.archlinux.org/title/Linux_console/Keyboard_configuration>`__!, sys_enc: `Character encoding <https://wiki.archlinux.org/title/Locale>`_!, sys_lang: `locale <https://wiki.archlinux.org/title/Locale>`_},Defines the keyboard key map!, system encoding and system locale,No
+mirror_config,{custom_mirrors: [ https://... ]!, mirror_regions: { "Worldwide": [ "https://geo.mirror.pkgbuild.com/$repo/os/$arch" ] } },Sets various mirrors *(defaults to ISO's ``/etc/pacman.d/mirrors`` if not defined)*,No
+network_config,*`see options under Network Configuration`*,Sets which type of *(if any)* network configuration should be used,No
+no_pkg_lookups,``true``!, ``false``,Disabled package checking against https://archlinux.org/packages/,No
+ntp,``true``!, ``false``,enables or disables `NTP <https://wiki.archlinux.org/title/Network_Time_Protocol_daemon>`_ during installation,No
+offline,``true``!, ``false``,enables or disables certain online checks such as mirror reachability etc,No
+packages,[ <package1>!, <package2>!, ... ],A list of packages to install during installation,No
+parallel downloads,0-∞,sets a given number of parallel downloads to be used by `pacman <https://wiki.archlinux.org/title/Pacman#Enabling_parallel_downloads>`_,No
+profile_config,*`read more under the profiles section`*,Installs a given profile if defined,No
+script,`guided <https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py>`__! *(default)*!, `minimal <https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/minimal.py>`__!, `only_hdd <https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/only_hdd.py>`_!, `swiss <https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/swiss.py>`_!, `unattended <https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/unattended.py>`_,When used to autorun an installation!, this sets which script to autorun with,No
+silent,``true``!, ``false``,disables or enables user questions using the TUI,No
+swap,``true``!, ``false``,enables or disables swap,No
+timezone,`timezone <https://wiki.archlinux.org/title/System_time#Time_zone>`_,sets a timezone for the installed system,No
diff --git a/docs/cli_parameters/config/custom_commands.rst b/docs/cli_parameters/config/custom_commands.rst
new file mode 100644
index 00000000..c1529020
--- /dev/null
+++ b/docs/cli_parameters/config/custom_commands.rst
@@ -0,0 +1,22 @@
+.. _custom commands:
+
+Custom Commands
+===============
+
+| Custom commands is a configuration entry that allows for executing custom commands post-installation.
+| The commands are executed with `arch-chroot <https://man.archlinux.org/man/extra/arch-install-scripts/arch-chroot.8.en>`_.
+
+The option takes a list of arguments, an example is:
+
+.. code-block:: json
+
+ {
+ "custom-commands": [
+ "hostname new-hostname"
+ ]
+ }
+
+| The following example will set a new hostname in the installed system.
+| The example is just to illustrate that the command is not run in the ISO but inside the installed system after the base system is installed.
+
+More examples can be found in the code repository under `examples/ <https://github.com/archlinux/archinstall/tree/e6344f93f7e476d05bbcd642f2ed91fdde545870/examples>`_ \ No newline at end of file
diff --git a/docs/cli_parameters/config/disk_config.rst b/docs/cli_parameters/config/disk_config.rst
new file mode 100644
index 00000000..b09d0dc0
--- /dev/null
+++ b/docs/cli_parameters/config/disk_config.rst
@@ -0,0 +1,235 @@
+.. _disk config:
+
+Disk Configuration
+==================
+
+There are only three modes in the ``disk_config`` option. They are described in more detail below.
+
+"Leave as is"
+--------------
+
+.. code-block:: json
+
+ {
+ "config_type": "pre_mounted_config",
+ "mountpoint": "/mnt/archinstall"
+ }
+
+This mode will not perform any partitioning what so ever.
+Instead it relies on what's mounted manually by the user under ``/mnt/archinstall``.
+
+Given the following disk example:
+
+.. code-block::
+
+ /mnt/archinstall (/dev/sda2)
+ ├── boot (/dev/sda1)
+ └── home (/dev/sda3)
+
+Running ``archinstall --conf your.json --silent`` where the above JSON is configured. The disk will be left alone — and a working system will be installed to the above folders and mountpoints will be translated into the installed system.
+
+.. note::
+
+ Some disk layouts can be too complicated to detect, such as RAID setups. Please do report those setups on the `Issue Tracker <https://github.com/archlinux/archinstall>`__ so we can support them.
+
+Best Effort
+-----------
+
+.. warning::
+
+ This mode will wipe data!
+
+.. note::
+
+ Note that this options is similar to the manual partitioning but is generated through the menu system! And the best effort layout might deviate slightly from some wiki guidelines in order to facilitate some optional configurations at a later stage.
+
+.. code-block:: json
+
+ {
+ "disk_config": {
+ "config_type": "default_layout",
+ "device_modifications": [
+ {
+ "device": "/dev/sda",
+ "wipe": true,
+ "partitions": "..."
+ }
+ ]
+ }
+ }
+
+This mode will attempt to configure a sane default layout on the selected disks.
+Based on the chosen filesystem, and potential optional settings for said filesystem — different default layouts will be provided.
+
+Manual Partitioning
+-------------------
+
+.. code-block:: json
+
+ {
+ "disk_config": {
+ "config_type": "manual_partitioning",
+ "device_modifications": [
+ "filesystem struct"
+ ]
+ }
+ }
+
+Manual partitioning is the most complex one of the three. It offers you near endless flexibility of how to partition your disk. It integrates against `pyparted <https://github.com/dcantrell/pyparted>`__ and some control logic in ``archinstall`` that deals with creating things like subvolumes and compression.
+
+Sizes are by default ``sector`` units, but other units are supported.
+
+The options supplied to ``manual_partitioning`` are dictionary definitions, where the following parameters must exist:
+
+.. csv-table:: JSON options
+ :file: ./manual_options.csv
+ :widths: 15, 15, 65, 5
+ :escape: !
+ :header-rows: 1
+
+Each partition definition heavily relies on what filesystem is used.
+Below follow two of the more common filesystems, anything else will best be described by running ``archinstall`` to generate a desired configuration for the desired filesystem type — and copy the relevant parts for permanent configurations.
+
+.. warning::
+
+ Important to note that the units and positions in the examples below — are highly user specific!
+
+FAT32
+^^^^^
+
+.. code-block:: json
+
+ {
+ "btrfs": [],
+ "flags": [
+ "Boot"
+ ],
+ "fs_type": "fat32",
+ "length": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 99982592
+ },
+ "mount_options": [],
+ "mountpoint": "/boot",
+ "obj_id": "369f31a8-2781-4d6b-96e7-75680552b7c9",
+ "start": {
+ "sector_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 512
+ },
+ "total_size": null,
+ "unit": "sectors",
+ "value": 34
+ },
+ "status": "create",
+ "type": "primary"
+ }
+
+.. note::
+
+ The ``Boot`` flag will make ``archinstall`` automatically set the correct ESP partition GUID if the system is booted with ``EFI`` support. The GUID will then be set to ``C12A7328-F81F-11D2-BA4B-00A0C93EC93B``.
+
+EXT4
+^^^^
+
+.. code-block:: json
+
+ {
+ "btrfs": [],
+ "flags": [],
+ "fs_type": "ext4",
+ "length": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 15805127360
+ },
+ "mount_options": [],
+ "mountpoint": "/",
+ "obj_id": "3e75d045-21a4-429d-897e-8ec19a006e8b",
+ "start": {
+ "sector_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 512
+ },
+ "total_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 16106127360
+ },
+ "unit": "MB",
+ "value": 301
+ },
+ "status": "create",
+ "type": "primary"
+ }
+
+BTRFS
+^^^^^
+
+The BTRFS filesystem is inherently more complicated, thus the options are a bit more involved.
+This example contains both subvolumes and compression.
+
+.. note::
+
+ Note that the ``"mountpoint": null`` is used for the overall partition, and instead individual subvolumes have mountpoints set.
+
+.. code-block:: json
+
+ {
+ "btrfs": [
+ {
+ "mountpoint": "/",
+ "name": "@",
+ },
+ {
+ "mountpoint": "/home",
+ "name": "@home",
+ },
+ {
+ "mountpoint": "/var/log",
+ "name": "@log",
+ },
+ {
+ "mountpoint": "/var/cache/pacman/pkg",
+ "name": "@pkg",
+ },
+ {
+ "mountpoint": "/.snapshots",
+ "name": "@.snapshots",
+ }
+ ],
+ "dev_path": null,
+ "flags": [],
+ "fs_type": "btrfs",
+ "mount_options": [
+ "compress=zstd"
+ ],
+ "mountpoint": null,
+ "obj_id": "d712357f-97cc-40f8-a095-24ff244d4539",
+ "size": {
+ "sector_size": {
+ "unit": "B",
+ "value": 512
+ },
+ "unit": "B",
+ "value": 15568207872
+ },
+ "start": {
+ "sector_size": {
+ "unit": "B",
+ "value": 512
+ },
+ "unit": "MiB",
+ "value": 513
+ },
+ "status": "create",
+ "type": "primary"
+ }
diff --git a/docs/cli_parameters/config/disk_encryption.rst b/docs/cli_parameters/config/disk_encryption.rst
new file mode 100644
index 00000000..df2e2fa7
--- /dev/null
+++ b/docs/cli_parameters/config/disk_encryption.rst
@@ -0,0 +1,19 @@
+.. _disk encryption:
+
+Disk Encryption
+===============
+
+Disk encryption consists of a top level entry in the user configuration.
+
+.. code-block:: json
+
+ {
+ "disk_encryption": {
+ "encryption_type": "luks",
+ "partitions": [
+ "d712357f-97cc-40f8-a095-24ff244d4539"
+ ]
+ }
+ }
+
+The ``UID`` in the ``partitions`` list is an internal reference to the ``obj_id`` in the :ref:`disk config` entries. \ No newline at end of file
diff --git a/docs/cli_parameters/config/manual_options.csv b/docs/cli_parameters/config/manual_options.csv
new file mode 100644
index 00000000..2fcc26f0
--- /dev/null
+++ b/docs/cli_parameters/config/manual_options.csv
@@ -0,0 +1,4 @@
+Key,Value(s),Description,Required
+device,``str``,Which block-device to format,yes
+partitions,[ {key: val} ],The data describing the change/addition in a partition,yes
+wipe,``bool``,clear the disk before adding any partitions,No \ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index 98c5e651..013cd2d2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -50,7 +50,8 @@ master_doc = 'index'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.inheritance_diagram',
- 'sphinx.ext.todo'
+ 'sphinx.ext.todo',
+ 'sphinx_rtd_theme'
]
# Add any paths that contain templates here, relative to this directory.
diff --git a/docs/examples/binary.rst b/docs/examples/binary.rst
deleted file mode 100644
index 51dbd1dd..00000000
--- a/docs/examples/binary.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-.. _examples.binary:
-
-Binary executable
-=================
-
-.. warning:: The binary option is limited and stiff. It's hard to modify or create your own installer-scripts this way unless you compile the source manually. If your usecase needs custom scripts, either use the pypi setup method or you'll need to adjust the PKGBUILD prior to building the arch package.
-
-The binary executable is a standalone compiled version of the library.
-It's compiled using `nuitka <https://nuitka.net/>`_ with the flag `--standalone`.
-
-Executing the binary
---------------------
-
-As an example we'll use the `guided <https://github.com/archlinux/archinstall/blob/master/examples/guided.py>`_ installer.
-To run the `guided` installed, all you have to do *(after installing or compiling the binary)*, is run:
-
-
-.. code-block:: console
-
- ./archinstall guided
-
-As mentioned, the binary is a bit rudimentary and only supports executing whatever is found directly under `./archinstall/examples`.
-Anything else won't be found. This is subject to change in the future to make it a bit more flexible.
diff --git a/docs/examples/python.rst b/docs/examples/python.rst
index dfc0abb3..7226c825 100644
--- a/docs/examples/python.rst
+++ b/docs/examples/python.rst
@@ -4,7 +4,7 @@ Python module
=============
Archinstall supports running in `module mode <https://docs.python.org/3/library/__main__.html>`_.
-The way the library is invoked in module mode is limited to executing scripts under the **example** folder.
+The way the library is invoked in module mode is limited to executing scripts under the `scripts`_ folder.
It's therefore important to place any script or profile you wish to invoke in the examples folder prior to building and installing.
@@ -12,7 +12,7 @@ Pre-requisites
--------------
We'll assume you've followed the :ref:`installing.python.manual` method.
-Before actually installing the library, you will need to place your custom installer-scripts under `./archinstall/examples/` as a python file.
+Before actually installing the library, you will need to place your custom installer-scripts under `scripts`_ as a python file.
More on how you create these in the next section.
@@ -24,36 +24,73 @@ Creating a script
-----------------
Lets create a `test_installer` - installer as an example. This is assuming that the folder `./archinstall` is a git-clone of the main repo.
-We begin by creating `./archinstall/examples/test_installer.py`. The placement here is important later.
+We begin by creating "`scripts`_:code:`/test_installer.py`". The placement here is important later.
-This script can now already be called using `python -m archinstall test_installer` after a successful installation of the library itself.
+This script can now already be called using :code:`python -m archinstall test_installer` after a successful installation of the library itself.
But the script won't do much. So we'll do something simple like list all the hard drives as an example.
-To do this, we'll begin by importing `archinstall` in our `./archinstall/examples/test_installer.py` and call some functions.
+To do this, we'll begin by importing :code:`archinstall` in our "`scripts`_:code:`/test_installer.py`" and call a function whtin ``archinstall``.
.. code-block:: python
import archinstall
-
- all_drives = archinstall.all_blockdevices(partitions=False)
- print(list(all_drives.keys()))
-This should print out a list of drives.
-As an example, this will do just fine.
+ print(archinstall.disk.device_handler.devices)
+
+Now, go ahead and reference the :ref:`installing.python.manual` installation method.
+After running ``python -m archinstall test_installer`` it should print something that looks like:
+
+.. code-block:: text
+
+ [
+ BDevice(
+ disk=<parted.disk.Disk object at 0x7fbe17156050>,
+ device_info=_DeviceInfo(
+ model='PC801 NVMe SK hynix 512GB',
+ path=PosixPath('/dev/nvme0n1'),
+ type='nvme',
+ total_size=Size(value=512110190592, unit=<Unit.B: 1>,
+ sector_size=SectorSize(value=512, unit=<Unit.B: 1>)),
+ free_space_regions=[
+ <archinstall.lib.disk.device_model.DeviceGeometry object at 0x7fbe166c4250>,
+ <archinstall.lib.disk.device_model.DeviceGeometry object at 0x7fbe166c4c50>,
+ <archinstall.lib.disk.device_model.DeviceGeometry object at 0x7fbe166c4a10>],
+ sector_size=SectorSize(value=512, unit=<Unit.B: 1>),
+ read_only=False,
+ dirty=False
+ ),
+ partition_infos=[
+ _PartitionInfo(
+ partition=<parted.partition.Partition object at 0x7fbe166c4a90>,
+ name='primary',
+ type=<PartitionType.Primary: 'primary'>,
+ fs_type=<FilesystemType.Fat32: 'fat32'>,
+ path='/dev/nvme0n1p1',
+ start=Size(value=2048, unit=<Unit.sectors: 'sectors'>, sector_size=SectorSize(value=512, unit=<Unit.B: 1>)),
+ length=Size(value=535822336, unit=<Unit.B: 1>, sector_size=SectorSize(value=512, unit=<Unit.B: 1>)),
+ flags=[
+ <PartitionFlag.Boot: 1>,
+ <PartitionFlag.ESP: 18>
+ ],
+ partn=1,
+ partuuid='a26be943-c193-41f4-9930-9341cf5f6b19',
+ uuid='6EE9-2C00',
+ disk=<parted.disk.Disk object at 0x7fbe17156050>,
+ mountpoints=[
+ PosixPath('/boot')
+ ],
+ btrfs_subvol_infos=[]
+ ),
+ _PartitionInfo(...)
+ ]
+ )
+ ]
+
+That means your script is in the right place, and ``archinstall`` is working as intended.
-Now, go ahead and install the library either as a user-module or system-wide.
-
-Calling a module
-----------------
-
-Assuming you've followed the example in `Creating a script`_, you can now safely call it with:
-
-.. code-block:: console
-
- python -m archinstall test_installer
+.. note::
-This should now print all available drives on your system.
+ Most calls, including the one above requires `root <https://en.wikipedia.org/wiki/Superuser>`_ privileges.
-.. note::
- This should work on any system, not just Arch Linux based ones. But note that other functions in the library rely heavily on Arch Linux based commands to execute the installation steps. Such as `arch-chroot`.
+.. _scripts: https://github.com/archlinux/archinstall/tree/master/archinstall/scripts
diff --git a/docs/help/known_issues.rst b/docs/help/known_issues.rst
new file mode 100644
index 00000000..622356c1
--- /dev/null
+++ b/docs/help/known_issues.rst
@@ -0,0 +1,102 @@
+.. _help.known_issues:
+
+Known Issues
+============
+
+| Some issues are out of the `archinstall`_ projects scope, and the ones we know of are listed below.
+
+.. _waiting for time sync:
+
+Waiting for time sync `#2144`_
+------------------------------
+
+| The usual root cause of this is the network topology.
+| More specifically `timedatectl show`_ cannot perform a proper time sync against the default servers.
+
+| Restarting ``systemd-timesyncd.service`` might work but most often you need to configure ``/etc/systemd/timesyncd.conf`` to match your network design.
+
+.. note::
+
+ If you know your time is correct on the machine, you can run ``archinstall --skip-ntp`` to ignore time sync.
+
+Missing Nvidia Proprietary Driver `#2002`_
+------------------------------------------
+
+| In some instances, the nvidia driver might not have all the necessary packages installed.
+| This is due to the kernel selection and/or hardware setups requiring additional packages to work properly.
+
+A common workaround is to install the package `linux-headers`_ and `nvidia-dkms`_
+
+ARM, 32bit and other CPU types error out `#1686`_, `#2185`_
+-----------------------------------------------------------
+
+| This is a bit of a catch-all known issue.
+| Officially `x86_64`_ is only supported by Arch Linux.
+| Hence little effort have been put into supporting other platforms.
+
+| In theory, other architectures should work but small quirks might arise.
+
+| PR's are welcome but please be respectful of the delays in merging.
+| Other fixes, issues or features will be prioritized for the above reasons.
+
+Keyring is out of date `#2213`_
+-------------------------------
+
+| Missing key-issues tend to be that the `archlinux-keyring`_ package is out of date, usually as a result of an outdated ISO.
+| There is an attempt from upstream to fix this issue, and it's the `archlinux-keyring-wkd-sync.service`_
+
+| The service starts almost immediately during boot, and if network is not configured in time — the service will fail.
+| Subsequently the ``archinstall`` run might operate on a old keyring despite there being an update service for this.
+
+| There is really no way to reliably over time work around this issue in ``archinstall``.
+| Instead, efforts to the upstream service should be considered the way forward. And/or keys not expiring between a sane amount of ISO's.
+
+.. note::
+
+ The issue can happen on new ISO's too even as little as a few days after release, as some keys might expire right after the keyring is *"burnt in"* to the ISO.
+
+.. note::
+
+ Another common issue relating to the network not being configured, is that time might not be set correctly - resulting in the keyring not being able to update. See :ref:`waiting for time sync`.
+
+AUR packages
+------------
+
+| This is also a catch-all issue.
+| `AUR is unsupported <https://wiki.archlinux.org/title/Arch_User_Repository#Updating_packages>`_, and until that changes we cannot use AUR packages to solve feature requests in ``archinstall``.
+
+| This means that feature requests like supporting filesystems such as `ZFS`_ can not be added, and issues cannot be solved by using AUR packages either.
+
+.. note::
+
+ But in spirit of giving the community options, ``archinstall`` supports :ref:`archinstall.Plugins`, which means you can run ``archinstall --plugin <url>`` and source an AUR plugin.
+
+ `torxed/archinstall-aur <https://github.com/torxed/archinstall-aur>`_ is a reference implementation for plugins:
+
+ .. code-block:: console
+
+ # archinstall --plugin https://archlinux.life/aur-plugin
+
+ `phisch/archinstall-aur <https://github.com/phisch/archinstall-aur>`_ is another alternative:
+
+ .. code-block:: console
+
+ # archinstall --plugin https://raw.githubusercontent.com/phisch/archinstall-aur/master/archinstall-aur.py
+
+ .. warning::
+
+ This will allow for unsupported usage of AUR during installation.
+
+.. _#2213: https://github.com/archlinux/archinstall/issues/2213
+.. _#2185: https://github.com/archlinux/archinstall/issues/2185
+.. _#2144: https://github.com/archlinux/archinstall/issues/2144
+.. _#2002: https://github.com/archlinux/archinstall/issues/2002
+.. _#1686: https://github.com/archlinux/archinstall/issues/1686
+.. _linux-headers: https://archlinux.org/packages/core/x86_64/linux-headers/
+.. _nvidia-dkms: https://archlinux.org/packages/extra/x86_64/nvidia-dkms/
+.. _x86_64: https://wiki.archlinux.org/title/Frequently_asked_questions#What_architectures_does_Arch_support?
+.. _archlinux-keyring: https://archlinux.org/packages/core/any/archlinux-keyring/
+.. _archlinux-keyring-wkd-sync.service: https://gitlab.archlinux.org/archlinux/archlinux-keyring/-/blob/7e672dad10652a80d1cc575d75cdb46442cd7f96/wkd_sync/archlinux-keyring-wkd-sync.service.in
+.. _ZFS: https://aur.archlinux.org/packages/zfs-linux
+.. _archinstall: https://github.com/archlinux/archinstall/
+.. _timedatectl show: https://github.com/archlinux/archinstall/blob/e6344f93f7e476d05bbcd642f2ed91fdde545870/archinstall/lib/installer.py#L136
diff --git a/docs/help/issues.rst b/docs/help/report_bug.rst
index 7d690b65..bd0ac50a 100644
--- a/docs/help/issues.rst
+++ b/docs/help/report_bug.rst
@@ -1,6 +1,6 @@
.. _help.issues:
-Issue tracker & bugs
+Report Issues & Bugs
====================
Issues and bugs should be reported over at `https://github.com/archlinux/archinstall/issues <https://github.com/Torxed/archinstall/issues>`_.
diff --git a/docs/index.rst b/docs/index.rst
index a76a58d6..6a81bbb0 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,17 +8,15 @@ archinstall Documentation
Some of the features of Archinstall are:
-* **No external dependencies or installation requirements.** Runs without any external requirements or installation processes.
-
* **Context friendly.** The library always executes calls in sequential order to ensure installation-steps don't overlap or execute in the wrong order. It also supports *(and uses)* context wrappers to ensure cleanup and final tasks such as ``mkinitcpio`` are called when needed.
-* **Full transparency** Logs and insights can be found at ``/var/log/archinstall`` both in the live ISO and the installed system.
+* **Full transparency** Logs and insights can be found at ``/var/log/archinstall`` both in the live ISO and partially on the installed system.
* **Accessibility friendly** Archinstall works with ``espeakup`` and other accessibility tools thanks to the use of a TUI.
.. toctree::
- :maxdepth: 3
- :caption: Running the installer
+ :maxdepth: 1
+ :caption: Running Archinstall
installing/guided
@@ -26,8 +24,9 @@ Some of the features of Archinstall are:
:maxdepth: 3
:caption: Getting help
+ help/known_issues
+ help/report_bug
help/discord
- help/issues
.. toctree::
:maxdepth: 3
@@ -35,35 +34,10 @@ Some of the features of Archinstall are:
installing/python
examples/python
-
-.. toctree::
- :maxdepth: 3
- :caption: Archinstall as a binary
-
- installing/binary
- examples/binary
-..
- examples/scripting
-
-..
- .. toctree::
- :maxdepth: 3
- :caption: Programming Guide
-
-..
- programming_guide/requirements
- programming_guide/basic_concept
+ archinstall/plugins
.. toctree::
:maxdepth: 3
:caption: API Reference
archinstall/Installer
- archinstall/Profile
- archinstall/Application
-
-.. toctree::
- :maxdepth: 3
- :caption: API Helper functions
-
- archinstall/general
diff --git a/docs/installing/binary.rst b/docs/installing/binary.rst
deleted file mode 100644
index eeb9d79d..00000000
--- a/docs/installing/binary.rst
+++ /dev/null
@@ -1,52 +0,0 @@
-.. _installing.binary:
-
-Binary executable
-=================
-
-Archinstall can be compiled into a standalone executable.
-For Arch Linux based systems, there's a package for this called `archinstall <https://archlinux.org/packages/extra/any/archinstall/>`_.
-
-.. warning::
- This is not required if you're running archinstall on a pre-built ISO. The installation is only required if you're creating your own scripted installations.
-
-Using pacman
-------------
-
-Archinstall is on the `official repositories <https://wiki.archlinux.org/index.php/Official_repositories>`_.
-
-.. code-block:: console
-
- sudo pacman -S archinstall
-
-Using PKGBUILD
---------------
-
-The `source <https://github.com/archlinux/archinstall>`_ contains a binary `PKGBUILD <https://github.com/Torxed/archinstall/tree/master/PKGBUILD/archinstall>`_ which can be either copied straight off the website or cloned using :code:`git clone https://github.com/Torxed/archinstall`.
-
-Once you've obtained the `PKGBUILD`, building it is pretty straight forward.
-
-.. code-block:: console
-
- makepkg -s
-
-Which should produce an `archinstall-X.x.z-1.pkg.tar.zst` which can be installed using:
-
-.. code-block:: console
-
- sudo pacman -U archinstall-X.x.z-1.pkg.tar.zst
-
-.. note::
-
- For a complete guide on the build process, please consult the `PKGBUILD on ArchWiki <https://wiki.archlinux.org/index.php/PKGBUILD>`_.
-
-Manual compilation
-------------------
-
-You can compile the source manually without using a custom mirror or the `PKGBUILD` that is shipped.
-Simply clone or download the source, and while standing in the cloned folder `./archinstall`, execute:
-
-.. code-block:: console
-
- nuitka3 --standalone --show-progress archinstall
-
-This requires the `nuitka <https://archlinux.org/packages/community/any/nuitka/>`_ package as well as `python3` to be installed locally.
diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst
index 4cb07ae1..90abedb4 100644
--- a/docs/installing/guided.rst
+++ b/docs/installing/guided.rst
@@ -3,20 +3,16 @@
Guided installation
===================
-| This is the default script the Arch Linux `Archinstall package <https://archlinux.org/packages/extra/any/archinstall/>`_.
-| It will guide you through a very basic installation of Arch Linux.
+Archinstall ships with a pre-programmed `Guided Installer`_ guiding you through the mandatory steps as well as some optional configurations that can be done.
.. note::
- There are other scripts and they can be invoked by executing `archinstall <script>` *(without .py)*. To see a complete list of scripts, see the source code directory `examples/ <https://github.com/archlinux/archinstall/tree/master/examples>`_
-The installer has three pre-requisites:
- * The latest version of `Arch Linux ISO <https://archlinux.org/download/>`_
- * A physical or virtual machine to install on
- * A `working internet connection <https://wiki.archlinux.org/title/installation_guide#Connect_to_the_internet>`_ prior to running archinstall
+ Other pre-programmed scripts can be invoked by executing :code:`archinstall <script>` *(without .py)*. To see a complete list of scripts, run :code:`archinstall --script list` or check the source code `scripts`_ directory.
.. note::
- A basic understanding of machines, ISO-files and command line arguments are needed.
- Please read the official `Arch Linux Wiki <https://wiki.archlinux.org/>`_ to learn more about your future operating system.
+
+ It's recommended to run ``archinstall`` from the official Arch Linux ISO.
+
.. warning::
The installer will not configure WiFi before the installation begins. You need to read up on `Arch Linux networking <https://wiki.archlinux.org/index.php/Network_configuration>`_ before you continue.
@@ -28,128 +24,216 @@ To start the installer, run the following in the latest Arch Linux ISO:
.. code-block:: sh
- archinstall --script guided
-
-| The ``--script guided`` argument is optional as it's the default behavior.
-| But this will use our most guided installation and if you skip all the option steps it will install a minimal Arch Linux experience.
+ archinstall
+
+Since the `Guided Installer`_ is the default script, this is the equivalent of running :code:`archinstall guided`
-Installing directly from a configuration file
----------------------------------------------
-| The guided installation also supports installing with pre-configured answers to all the guided steps.
-| This can be a quick and convenient way to re-run one or several installations.
-|
-| After each successful installation a pre-configured configuration will be found at ``/var/log/archinstall`` both on the live media and the installed system.
+The guided installation also supports installing with pre-configured answers to all the guided steps. This can be a quick and convenient way to re-run one or several installations.
-There are three different configuration files, all of which are optional.
- * ``--config`` that deals with the general configuration of language and which profiles to use.
- * ``--creds`` which takes any ``superuser``, ``user`` or ``root`` account data.
- * ``--disk_layouts`` for defining the desired partition strategy on the selected ``"harddrives"`` in ``--config``.
+There are two configuration files, both are optional.
+
+``--config``
+------------
+
+This parameter takes a local or remote :code:`.json` file as argument and contains the overall configuration and menu answers for the guided installer.
.. note::
- You can always get the latest options with ``archinstall --dry-run``, but edit the following json according to your needs.
- Save the configuration as a ``.json`` file. Archinstall can source it via a local or remote path (URL)
-
-.. code-block:: json
- {
- "audio": "pipewire",
- "bootloader": "systemd-bootctl",
- "custom-commands": [
- "cd /home/devel; git clone https://aur.archlinux.org/paru.git",
- "chown -R devel:devel /home/devel/paru",
- "usermod -aG docker devel"
- ],
- "filesystem": "btrfs",
- "gfx_driver": "VMware / VirtualBox (open-source)",
- "harddrives": [
- "/dev/nvme0n1"
- ],
- "swap": true,
- "hostname": "development-box",
- "kernels": [
- "linux"
- ],
- "keyboard-language": "us",
- "mirror-region": "Worldwide",
- "nic": {
- "type": "NM"
- },
- "ntp": true,
- "packages": ["docker", "git", "wget", "zsh"],
- "profile": "gnome",
- "services": ["docker"],
- "sys-encoding": "utf-8",
- "sys-language": "en_US",
- "timezone": "US/Eastern",
- }
+ You can always get the latest options for this file with ``archinstall --dry-run``, this executes the guided installer in a safe mode where no permanent actions will be taken on your system but simulate a run and save the configuration to disk.
-To use it, assuming you put it on ``https://domain.lan/config.json``:
+Example usage
+^^^^^^^^^^^^^
.. code-block:: sh
archinstall --config https://domain.lan/config.json
-Options for ``--config``
-------------------------
-
-*(To see which keys are required, scroll to the right in the above table.)*
-
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| Key | Values | Description | Required |
-| | | | |
-+======================+========================================================+=============================================================================================+===============================================+
-| audio | pipewire/pulseaudio | Audioserver to be installed | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| bootloader | systemd-bootctl/grub-install | Bootloader to be installed *(grub being mandatory on BIOS machines)* | Yes |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| custom-commands | [ <command1>, <command2>, ...] | Custom commands to be run post install | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| gfx_driver | - "VMware / VirtualBox (open-source)" | Graphics Drivers to install | No |
-| | - "Nvidia" | | |
-| | - "Intel (open-source)" | | |
-| | - "AMD / ATI (open-source)" | | |
-| | - "All open-source (default)" | | |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| harddrives | [ <path of device>, <path of second device>, ... } | Multiple paths to block devices to be formatted | No[1] |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| hostname | any | Hostname of machine after installation. Default will be ``archinstall`` | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| kernels | [ "kernel1", "kernel2"] | List of kernels to install eg: linux, linux-lts, linux-zen etc | At least 1 |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| keyboard-layout | Any valid layout given by ``localectl list-keymaps`` | eg: ``us``, ``de`` or ``de-latin1`` etc. Defaults to ``us`` | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| mirror-region | | {"<Region Name>": { "Mirror URL": True/False}, ..} | | Defaults to automatic selection. | No |
-| | | "Worldwide" or "Sweden" | | Either takes a dictionary structure of region and a given set of mirrors. | |
-| | | | Or just a region and archinstall will source any mirrors for that region automatically | |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| nic | | { type: <ISO|NM|MANUAL> } | | Type must be one of ISO, NM, MANUAL. ISO will copy the configuration on the image, | No |
-| | | | | NM configures NetworkManager and MANUAL allows to specify custom configuration | |
-| | | { "iface": "eth0"} | | Only MANUAL: name of the interface | |
-| | | { "dhcp": <boolean>} | | Only MANUAL: If set to true DHCP auto will be setup and all further configs ignored | |
-| | | { "ip": <ip>} | | Only MANUAL: Ip address to set, is MANDATORY | |
-| | | { "gateway": <ip>} | | Only MANUAL: Optional gateway | |
-| | | { "dns": [<ip>]} | | Only MANUAL: Optional DNS servers | |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| ntp | <boolean> | Set to true to set-up ntp post install | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| packages | [ "package1", "package2", ..] | List of packages to install post-installation | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| profile | Name of the profile to install | Profiles are present in | No |
-| | | `profiles/ <https://github.com/archlinux/archinstall/tree/master/profiles>`_, | |
-| | | use the name of a profile to install it without the ``.py`` extension. | |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| services | [ "service1", "service2", ..] | Services to enable post-installation | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| sys-encoding | "utf-8" | Set to change system encoding post-install, ignored if --advanced flag is not passed | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| sys-language | "en_US" | Set to change system language post-install, ignored if --advanced flag is not passed | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
-| timezone | Timezone to configure in installation | Timezone eg: UTC, Asia/Kolkata etc. Defaults to UTC | No |
-+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
+The contents of :code:`https://domain.lan/config.json`:
+
+.. code-block:: json
+
+ {
+ "__separator__": null,
+ "additional-repositories": [],
+ "archinstall-language": "English",
+ "audio_config": null,
+ "bootloader": "Systemd-boot",
+ "config_version": "2.6.0",
+ "debug": false,
+ "disk_config": {
+ "config_type": "manual_partitioning",
+ "device_modifications": [
+ {
+ "device": "/dev/sda",
+ "partitions": [
+ {
+ "btrfs": [],
+ "flags": [
+ "Boot"
+ ],
+ "fs_type": "fat32",
+ "length": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 99982592
+ },
+ "mount_options": [],
+ "mountpoint": "/boot",
+ "obj_id": "369f31a8-2781-4d6b-96e7-75680552b7c9",
+ "start": {
+ "sector_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 512
+ },
+ "total_size": null,
+ "unit": "sectors",
+ "value": 34
+ },
+ "status": "create",
+ "type": "primary"
+ },
+ {
+ "btrfs": [],
+ "flags": [],
+ "fs_type": "fat32",
+ "length": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 100000000
+ },
+ "mount_options": [],
+ "mountpoint": "/efi",
+ "obj_id": "13cf2c96-8b0f-4ade-abaa-c530be589aad",
+ "start": {
+ "sector_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 512
+ },
+ "total_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 16106127360
+ },
+ "unit": "MB",
+ "value": 100
+ },
+ "status": "create",
+ "type": "primary"
+ },
+ {
+ "btrfs": [],
+ "flags": [],
+ "fs_type": "ext4",
+ "length": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 15805127360
+ },
+ "mount_options": [],
+ "mountpoint": "/",
+ "obj_id": "3e75d045-21a4-429d-897e-8ec19a006e8b",
+ "start": {
+ "sector_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 512
+ },
+ "total_size": {
+ "sector_size": null,
+ "total_size": null,
+ "unit": "B",
+ "value": 16106127360
+ },
+ "unit": "MB",
+ "value": 301
+ },
+ "status": "create",
+ "type": "primary"
+ }
+ ],
+ "wipe": false
+ }
+ ]
+ },
+ "disk_encryption": {
+ "encryption_type": "luks",
+ "partitions": [
+ "3e75d045-21a4-429d-897e-8ec19a006e8b"
+ ]
+ },
+ "hostname": "archlinux",
+ "kernels": [
+ "linux"
+ ],
+ "locale_config": {
+ "kb_layout": "us",
+ "sys_enc": "UTF-8",
+ "sys_lang": "en_US"
+ },
+ "mirror_config": {
+ "custom_mirrors": [],
+ "mirror_regions": {
+ "Sweden": [
+ "https://mirror.osbeck.com/archlinux/$repo/os/$arch",
+ "https://mirror.bahnhof.net/pub/archlinux/$repo/os/$arch",
+ "https://ftp.myrveln.se/pub/linux/archlinux/$repo/os/$arch",
+ "https://ftp.lysator.liu.se/pub/archlinux/$repo/os/$arch",
+ "https://ftp.ludd.ltu.se/mirrors/archlinux/$repo/os/$arch",
+ "https://ftp.acc.umu.se/mirror/archlinux/$repo/os/$arch",
+ "http://mirror.bahnhof.net/pub/archlinux/$repo/os/$arch",
+ "http://ftpmirror.infania.net/mirror/archlinux/$repo/os/$arch",
+ "http://ftp.myrveln.se/pub/linux/archlinux/$repo/os/$arch",
+ "http://ftp.lysator.liu.se/pub/archlinux/$repo/os/$arch",
+ "http://ftp.acc.umu.se/mirror/archlinux/$repo/os/$arch"
+ ]
+ }
+ },
+ "network_config": {},
+ "no_pkg_lookups": false,
+ "ntp": true,
+ "offline": false,
+ "packages": [],
+ "parallel downloads": 0,
+ "profile_config": null,
+ "save_config": null,
+ "script": "guided",
+ "silent": false,
+ "swap": true,
+ "timezone": "UTC",
+ "version": "2.6.0"
+ }
+
+``--config`` options
+^^^^^^^^^^^^^^^^^^^^
+
+.. warning::
+
+ All key and value entries must conform to the JSON standard. Below is human readable examples with links, effectively breaking the syntax. Adapt the descriptions below to suit your needs and the JSON format.
+
+.. note::
+
+ Scroll to the right in the table to see required options.
+
+.. csv-table:: JSON options
+ :file: ../cli_parameters/config/config_options.csv
+ :widths: 15, 40, 40, 5
+ :escape: !
+ :header-rows: 1
+.. I'd like to keep this note, as this is the intended behavior of archinstall.
.. note::
- [1] If no entries are found in ``harddrives``, archinstall guided installation will use whatever is mounted currently under ``/mnt/archinstall``.
+
+ If no entries are found in ``disk_config``, archinstall guided installation will use whatever is mounted currently under ``/mnt/archinstall`` without performing any disk operations.
Options for ``--creds``
-----------------------
@@ -163,88 +247,41 @@ Options for ``--creds``
"!root-password" : "SecretSanta2022"
}
-+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| Key | Values | Description | Required |
-+======================+========================================================+======================================================================================+===============================================+
-| !encryption-password | any | Password to encrypt disk, not encrypted if password not provided | No |
-+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| !root-password | any | The root account password | No |
-+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| !users | { "username": "<USERNAME>" | List of regular user credentials, see configuration for reference | No |
-| | "!password": "<PASSWORD>", | | |
-| | "sudo": false|true} | | |
-+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
+.. list-table:: --creds options
+ :widths: 25 25 40 10
+ :header-rows: 1
+
+ * - Key
+ - Values
+ - Description
+ - Required
+ * - !encryption-password
+ - ``str``
+ - Password to encrypt disk, not encrypted if password not provided
+ - No
+ * - !root-password
+ - ``str``
+ - The root account password
+ - No
+ * - !users
+ - .. code-block:: json
+
+ {
+ "username": "<USERNAME>",
+ "!password": "<PASSWORD>",
+ "sudo": false
+ }
+ - List of regular user credentials, see configuration for reference
+ - Maybe
+
.. note::
- [1] ``!users`` is optional only if ``!root-password`` was set. ``!users`` will be enforced otherwise and the minimum amount of users with sudo privileges required will be set to 1.
-Options for ``--disk_layouts``
-------------------------------
+ ``!users`` is optional only if ``!root-password`` was set. ``!users`` will be enforced otherwise and the minimum amount of users with sudo privileges required will be set to 1.
.. note::
- | The layout of ``--disk_layouts`` is a bit complicated.
- | It's highly recommended that you generate it using ``--dry-run`` which will simulate an installation, without performing any damaging actions on your machine. *(no formatting is done)*
-
-.. code-block:: json
- {
- "/dev/loop0": {
- "partitions": [
- {
- "boot": true,
- "encrypted": false,
- "filesystem": {
- "format": "fat32"
- },
- "wipe": true,
- "mountpoint": "/boot",
- "size": "513MB",
- "start": "5MB",
- "type": "primary"
- },
- {
- "btrfs": {
- "subvolumes": {
- "@.snapshots": "/.snapshots",
- "@home": "/home",
- "@log": "/var/log",
- "@pkgs": "/var/cache/pacman/pkg"
- }
- },
- "encrypted": true,
- "filesystem": {
- "format": "btrfs"
- },
- "wipe": true,
- "mountpoint": "/",
- "size": "100%",
- "start": "518MB",
- "type": "primary"
- }
- ],
- "wipe": true
- }
- }
+ The key's start with ``!`` because internal log functions will mask any keys starting with explamation from logs and unrestricted configurations.
-| The overall structure is that of ``{ "blockdevice-path" : ...}`` followed by options for that blockdevice.
-| Each partition has it's own settings, and the formatting is executed in order *(top to bottom in the above example)*.
-| Mountpoints is later mounted in order of path traversal, ``/`` before ``/home`` etc.
-
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| Key | Values | Description | Required |
-| | | | |
-+======================+=====================================================+======================================================================================+===============================================+
-| filesystem | { "format": "ext4 / btrfs / fat32 etc." } | Filesystem for root and other partitions | Yes |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| boot | <bool> | Marks the partition as bootable | No |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| encrypted | <bool> | Mark the partition for encryption | No |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| mountpoint | /path | Relative to the inside of the installation, where should the partition be mounted | Yes |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| start | <size><B, MiB, GiB, %, etc> | The start position of the partition | Yes |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| type | primary | Only used if MBR and BIOS is used. Marks what kind of partition it is. | No |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
-| btrfs | { "subvolumes": {"subvolume": "mountpoint"}} | Support for btrfs subvolumes for a given partition | No |
-+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+
+.. _scripts: https://github.com/archlinux/archinstall/tree/master/archinstall/scripts
+.. _Guided Installer: https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py
diff --git a/docs/installing/python.rst b/docs/installing/python.rst
index cf4f7882..edd55138 100644
--- a/docs/installing/python.rst
+++ b/docs/installing/python.rst
@@ -50,8 +50,10 @@ You can either move the folder into your project and simply do
import archinstall
-Or you can use `setuptools <https://pypi.org/project/setuptools/>`_ to install it into the module path.
+Or you can PyPa's `build <https://github.com/pypa/build>`_ and `installer <https://github.com/pypa/installer>`_ to install it into pythons module path.
.. code-block:: console
- sudo python setup.py install \ No newline at end of file
+ $ cd archinstall
+ $ python -m build .
+ $ python -m installer dist/*.whl \ No newline at end of file
diff --git a/examples/auto_discovery_mounted.py b/examples/auto_discovery_mounted.py
new file mode 100644
index 00000000..e3cb80b7
--- /dev/null
+++ b/examples/auto_discovery_mounted.py
@@ -0,0 +1,12 @@
+from pathlib import Path
+
+from archinstall import disk
+
+root_mount_dir = Path('/mnt/archinstall')
+
+mods = disk.device_handler.detect_pre_mounted_mods(root_mount_dir)
+
+disk_config = disk.DiskLayoutConfiguration(
+ disk.DiskLayoutType.Pre_mount,
+ device_modifications=mods,
+)
diff --git a/examples/config-sample.json b/examples/config-sample.json
index dc8693a7..47a4e2e0 100644
--- a/examples/config-sample.json
+++ b/examples/config-sample.json
@@ -1,28 +1,121 @@
{
- "audio": null,
- "bootloader": "systemd-bootctl",
- "harddrives": [
- "/dev/loop0"
- ],
- "hostname": "",
+ "config_version": "2.5.2",
+ "additional-repositories": [],
+ "archinstall-language": "English",
+ "audio_config": {"audio": "pipewire"},
+ "bootloader": "Systemd-boot",
+ "debug": false,
+ "disk_config": {
+ "config_type": "default_layout",
+ "device_modifications": [
+ {
+ "device": "/dev/sda",
+ "partitions": [
+ {
+ "btrfs": [],
+ "flags": [
+ "Boot"
+ ],
+ "fs_type": "fat32",
+ "size": {
+ "sector_size": null,
+ "unit": "MiB",
+ "value": 512
+ },
+ "mount_options": [],
+ "mountpoint": "/boot",
+ "obj_id": "2c3fa2d5-2c79-4fab-86ec-22d0ea1543c0",
+ "start": {
+ "sector_size": null,
+ "unit": "MiB",
+ "value": 1
+ },
+ "status": "create",
+ "type": "primary"
+ },
+ {
+ "btrfs": [],
+ "flags": [],
+ "fs_type": "ext4",
+ "size": {
+ "sector_size": null,
+ "unit": "GiB",
+ "value": 20
+ },
+ "mount_options": [],
+ "mountpoint": "/",
+ "obj_id": "3e7018a0-363b-4d05-ab83-8e82d13db208",
+ "start": {
+ "sector_size": null,
+ "unit": "MiB",
+ "value": 513
+ },
+ "status": "create",
+ "type": "primary"
+ },
+ {
+ "btrfs": [],
+ "flags": [],
+ "fs_type": "ext4",
+ "size": {
+ "sector_size": null,
+ "unit": "Percent",
+ "value": 100
+ },
+ "mount_options": [],
+ "mountpoint": "/home",
+ "obj_id": "ce58b139-f041-4a06-94da-1f8bad775d3f",
+ "start": {
+ "sector_size": null,
+ "unit": "GiB",
+ "value": 20
+ },
+ "status": "create",
+ "type": "primary"
+ }
+ ],
+ "wipe": true
+ }
+ ]
+ },
+ "hostname": "archlinux",
"kernels": [
"linux"
],
"keyboard-layout": "us",
"mirror-region": {
- "Worldwide": {
- "https://mirror.rackspace.com/archlinux/$repo/os/$arch": true
+ "Australia": {
+ "http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch": true,
}
},
"nic": {
- "type": "NM"
+ "dhcp": true,
+ "dns": null,
+ "gateway": null,
+ "iface": null,
+ "ip": null,
+ "type": "nm"
},
+ "no_pkg_lookups": false,
"ntp": true,
+ "offline": false,
"packages": [],
- "profile": null,
+ "parallel downloads": 0,
+ "profile_config": {
+ "gfx_driver": "All open-source (default)",
+ "greeter": "sddm",
+ "profile": {
+ "details": [
+ "KDE Plasma"
+ ],
+ "main": "Desktop"
+ }
+ },
"script": "guided",
+ "silent": false,
"swap": true,
"sys-encoding": "utf-8",
"sys-language": "en_US",
- "timezone": "UTC"
+ "timezone": "UTC",
+ "version": "2.5.2"
}
diff --git a/examples/creds-sample.json b/examples/creds-sample.json
index 0681e16f..530a2465 100644
--- a/examples/creds-sample.json
+++ b/examples/creds-sample.json
@@ -1,15 +1,8 @@
{
- "!root-password": "<root password>",
- "!users": [
- {
- "username": "<USERNAME>",
- "!password": "<PASSWORD>",
- "sudo": false
- },
- {
- "username": "<SUDO_USERNAME>",
- "!password": "<PASSWORD>",
- "sudo": true
- }
- ]
+ "!users": [
+ {
+ "sudo": true,
+ "username": "archinstall"
+ }
+ ]
}
diff --git a/examples/custom-command-sample.json b/examples/custom-command-sample.json
index 8d8d611d..34d63d74 100644
--- a/examples/custom-command-sample.json
+++ b/examples/custom-command-sample.json
@@ -1,6 +1,5 @@
{
"dry_run": true,
- "audio": "none",
"bootloader": "systemd-bootctl",
"debug": false,
"harddrives": [
@@ -12,8 +11,8 @@
],
"keyboard-layout": "us",
"mirror-region": "Worldwide",
- "nic": {
- "type": "NM"
+ "network_config": {
+ "type": "nm"
},
"ntp": true,
"packages": ["docker", "git", "wget", "zsh"],
diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py
new file mode 100644
index 00000000..d25575d4
--- /dev/null
+++ b/examples/full_automated_installation.py
@@ -0,0 +1,99 @@
+from pathlib import Path
+
+from archinstall import Installer
+from archinstall import profile
+from archinstall.default_profiles.minimal import MinimalProfile
+from archinstall import disk
+from archinstall import models
+
+# we're creating a new ext4 filesystem installation
+fs_type = disk.FilesystemType('ext4')
+device_path = Path('/dev/sda')
+
+# get the physical disk device
+device = disk.device_handler.get_device(device_path)
+
+if not device:
+ raise ValueError('No device found for given path')
+
+# create a new modification for the specific device
+device_modification = disk.DeviceModification(device, wipe=True)
+
+# create a new boot partition
+boot_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=disk.Size(1, disk.Unit.MiB, device.device_info.sector_size),
+ length=disk.Size(512, disk.Unit.MiB, device.device_info.sector_size),
+ mountpoint=Path('/boot'),
+ fs_type=disk.FilesystemType.Fat32,
+ flags=[disk.PartitionFlag.Boot]
+)
+device_modification.add_partition(boot_partition)
+
+# create a root partition
+root_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=disk.Size(513, disk.Unit.MiB, device.device_info.sector_size),
+ length=disk.Size(20, disk.Unit.GiB, device.device_info.sector_size),
+ mountpoint=None,
+ fs_type=fs_type,
+ mount_options=[],
+)
+device_modification.add_partition(root_partition)
+
+start_home = root_partition.length
+length_home = device.device_info.total_size - start_home
+
+# create a new home partition
+home_partition = disk.PartitionModification(
+ status=disk.ModificationStatus.Create,
+ type=disk.PartitionType.Primary,
+ start=start_home,
+ length=length_home,
+ mountpoint=Path('/home'),
+ fs_type=fs_type,
+ mount_options=[]
+)
+device_modification.add_partition(home_partition)
+
+disk_config = disk.DiskLayoutConfiguration(
+ config_type=disk.DiskLayoutType.Default,
+ device_modifications=[device_modification]
+)
+
+# disk encryption configuration (Optional)
+disk_encryption = disk.DiskEncryption(
+ encryption_password="enc_password",
+ encryption_type=disk.EncryptionType.Luks,
+ partitions=[home_partition],
+ hsm_device=None
+)
+
+# initiate file handler with the disk config and the optional disk encryption config
+fs_handler = disk.FilesystemHandler(disk_config, disk_encryption)
+
+# perform all file operations
+# WARNING: this will potentially format the filesystem and delete all data
+fs_handler.perform_filesystem_operations(show_countdown=False)
+
+mountpoint = Path('/tmp')
+
+with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=['linux']
+) as installation:
+ installation.mount_ordered_layout()
+ installation.minimal_installation(hostname='minimal-arch')
+ installation.add_additional_packages(['nano', 'wget', 'git'])
+
+# Optionally, install a profile of choice.
+# In this case, we install a minimal profile that is empty
+profile_config = profile.ProfileConfiguration(MinimalProfile())
+profile.profile_handler.install_profile_config(installation, profile_config)
+
+user = models.User('archinstall', 'password', True)
+installation.create_users(user)
diff --git a/examples/guided.py b/examples/guided.py
deleted file mode 100644
index e9240c03..00000000
--- a/examples/guided.py
+++ /dev/null
@@ -1,306 +0,0 @@
-import logging
-import os
-import time
-
-import archinstall
-from archinstall import ConfigurationOutput, Menu
-from archinstall.lib.models.network_configuration import NetworkConfigurationHandler
-
-if archinstall.arguments.get('help'):
- print("See `man archinstall` for help.")
- exit(0)
-if os.getuid() != 0:
- print(_("Archinstall requires root privileges to run. See --help for more."))
- exit(1)
-
-# Log various information about hardware before starting the installation. This might assist in troubleshooting
-archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG)
-archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG)
-archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG)
-archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG)
-archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG)
-
-# For support reasons, we'll log the disk layout pre installation to match against post-installation layout
-archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
-
-
-def ask_user_questions():
- """
- First, we'll ask the user for a bunch of user input.
- Not until we're satisfied with what we want to install
- will we continue with the actual installation steps.
- """
-
- # ref: https://github.com/archlinux/archinstall/pull/831
- # we'll set NTP to true by default since this is also
- # the default value specified in the menu options; in
- # case it will be changed by the user we'll also update
- # the system immediately
- global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments)
-
- global_menu.enable('archinstall-language')
-
- global_menu.enable('keyboard-layout')
-
- # Set which region to download packages from during the installation
- global_menu.enable('mirror-region')
-
- global_menu.enable('sys-language')
- global_menu.enable('sys-encoding')
-
- # Ask which harddrives/block-devices we will install to
- # and convert them into archinstall.BlockDevice() objects.
- global_menu.enable('harddrives')
-
- global_menu.enable('disk_layouts')
-
- # Specify disk encryption options
- global_menu.enable('disk_encryption')
-
- # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB)
- global_menu.enable('bootloader')
-
- global_menu.enable('swap')
-
- # Get the hostname for the machine
- global_menu.enable('hostname')
-
- # Ask for a root password (optional, but triggers requirement for super-user if skipped)
- global_menu.enable('!root-password')
-
- global_menu.enable('!users')
-
- # Ask for archinstall-specific profiles (such as desktop environments etc)
- global_menu.enable('profile')
-
- # Ask about audio server selection if one is not already set
- global_menu.enable('audio')
-
- # Ask for preferred kernel:
- global_menu.enable('kernels')
-
- global_menu.enable('packages')
-
- if archinstall.arguments.get('advanced', False):
- # Enable parallel downloads
- global_menu.enable('parallel downloads')
-
- # Ask or Call the helper function that asks the user to optionally configure a network.
- global_menu.enable('nic')
-
- global_menu.enable('timezone')
-
- global_menu.enable('ntp')
-
- global_menu.enable('additional-repositories')
-
- global_menu.enable('__separator__')
-
- global_menu.enable('save_config')
- global_menu.enable('install')
- global_menu.enable('abort')
-
- global_menu.run()
-
-
-def perform_filesystem_operations():
- """
- Issue a final warning before we continue with something un-revertable.
- We mention the drive one last time, and count from 5 to 0.
- """
-
- if archinstall.arguments.get('harddrives', None):
- print(_(f" ! Formatting {archinstall.arguments['harddrives']} in "), end='')
- archinstall.do_countdown()
-
- """
- Setup the blockdevice, filesystem (and optionally encryption).
- Once that's done, we'll hand over to perform_installation()
- """
- mode = archinstall.GPT
- if archinstall.has_uefi() is False:
- mode = archinstall.MBR
-
- for drive in archinstall.arguments.get('harddrives', []):
- if archinstall.arguments.get('disk_layouts', {}).get(drive.path):
- with archinstall.Filesystem(drive, mode) as fs:
- fs.load_layout(archinstall.arguments['disk_layouts'][drive.path])
-
-
-def perform_installation(mountpoint):
- """
- Performs the installation steps on a block device.
- Only requirement is that the block devices are
- formatted and setup prior to entering this function.
- """
-
- with archinstall.Installer(mountpoint, kernels=archinstall.arguments.get('kernels', ['linux'])) as installation:
- # Mount all the drives to the desired mountpoint
- # This *can* be done outside of the installation, but the installer can deal with it.
- if archinstall.arguments.get('disk_layouts'):
- installation.mount_ordered_layout(archinstall.arguments['disk_layouts'])
-
- # Placing /boot check during installation because this will catch both re-use and wipe scenarios.
- for partition in installation.partitions:
- if partition.mountpoint == installation.target + '/boot':
- if partition.size < 0.19: # ~200 MiB in GiB
- raise archinstall.DiskError(f"The selected /boot partition in use is not large enough to properly install a boot loader. Please resize it to at least 200MiB and re-run the installation.")
-
- # If we've activated NTP, make sure it's active in the ISO too and
- # make sure at least one time-sync finishes before we continue with the installation
- if archinstall.arguments.get('ntp', False):
- # Activate NTP in the ISO
- archinstall.SysCommand('timedatectl set-ntp true')
-
- # TODO: This block might be redundant, but this service is not activated unless
- # `timedatectl set-ntp true` is executed.
- logged = False
- while archinstall.service_state('dbus-org.freedesktop.timesync1.service') not in ('running'):
- if not logged:
- installation.log(f"Waiting for dbus-org.freedesktop.timesync1.service to enter running state", level=logging.INFO)
- logged = True
- time.sleep(1)
-
- logged = False
- while 'Server: n/a' in archinstall.SysCommand('timedatectl timesync-status --no-pager --property=Server --value'):
- if not logged:
- installation.log(f"Waiting for timedatectl timesync-status to report a timesync against a server", level=logging.INFO)
- logged = True
- time.sleep(1)
-
- # if len(mirrors):
- # Certain services might be running that affects the system during installation.
- # Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist
- # We need to wait for it before we continue since we opted in to use a custom mirror/region.
- installation.log('Waiting for automatic mirror selection (reflector) to complete.', level=logging.INFO)
- while archinstall.service_state('reflector') not in ('dead', 'failed', 'exited'):
- time.sleep(1)
-
- installation.log('Waiting pacman-init.service to complete.', level=logging.INFO)
- while archinstall.service_state('pacman-init') not in ('dead', 'failed', 'exited'):
- time.sleep(1)
-
- installation.log('Waiting Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.', level=logging.INFO)
- while archinstall.service_state('archlinux-keyring-wkd-sync') not in ('dead', 'failed', 'exited'):
- time.sleep(1)
-
- # Set mirrors used by pacstrap (outside of installation)
- if archinstall.arguments.get('mirror-region', None):
- archinstall.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium
-
- # Retrieve list of additional repositories and set boolean values appropriately
- if archinstall.arguments.get('additional-repositories', None) is not None:
- enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', None)
- enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', None)
- else:
- enable_testing = False
- enable_multilib = False
-
- if installation.minimal_installation(
- testing=enable_testing, multilib=enable_multilib, hostname=archinstall.arguments['hostname'],
- locales=[f"{archinstall.arguments['sys-language']} {archinstall.arguments['sys-encoding'].upper()}"]):
- if archinstall.arguments.get('mirror-region') is not None:
- if archinstall.arguments.get("mirrors", None) is not None:
- installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium
- if archinstall.arguments.get('swap'):
- installation.setup_swap('zram')
- if archinstall.arguments.get("bootloader") == "grub-install" and archinstall.has_uefi():
- installation.add_additional_packages("grub")
- installation.add_bootloader(archinstall.arguments["bootloader"])
-
- # If user selected to copy the current ISO network configuration
- # Perform a copy of the config
- network_config = archinstall.arguments.get('nic', None)
-
- if network_config:
- handler = NetworkConfigurationHandler(network_config)
- handler.config_installer(installation)
-
- if archinstall.arguments.get('audio', None) is not None:
- installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=logging.INFO)
- if archinstall.arguments.get('audio', None) == 'pipewire':
- archinstall.Application(installation, 'pipewire').install()
- elif archinstall.arguments.get('audio', None) == 'pulseaudio':
- print('Installing pulseaudio ...')
- installation.add_additional_packages("pulseaudio")
- else:
- installation.log("No audio server will be installed.", level=logging.INFO)
-
- if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '':
- installation.add_additional_packages(archinstall.arguments.get('packages', None))
-
- if archinstall.arguments.get('profile', None):
- installation.install_profile(archinstall.arguments.get('profile', None))
-
- if users := archinstall.arguments.get('!users', None):
- installation.create_users(users)
-
- if timezone := archinstall.arguments.get('timezone', None):
- installation.set_timezone(timezone)
-
- if archinstall.arguments.get('ntp', False):
- installation.activate_time_syncronization()
-
- if archinstall.accessibility_tools_in_use():
- installation.enable_espeakup()
-
- if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
- installation.user_set_pw('root', root_pw)
-
- # This step must be after profile installs to allow profiles to install language pre-requisits.
- # After which, this step will set the language both for console and x11 if x11 was installed for instance.
- installation.set_keyboard_language(archinstall.arguments['keyboard-layout'])
-
- if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_post_install():
- with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported:
- if not imported._post_install():
- archinstall.log(' * Profile\'s post configuration requirements was not fulfilled.', fg='red')
- exit(1)
-
- # If the user provided a list of services to be enabled, pass the list to the enable_service function.
- # Note that while it's called enable_service, it can actually take a list of services and iterate it.
- if archinstall.arguments.get('services', None):
- installation.enable_service(*archinstall.arguments['services'])
-
- # If the user provided custom commands to be run post-installation, execute them now.
- if archinstall.arguments.get('custom-commands', None):
- archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation)
-
- installation.genfstab()
-
- installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow")
- if not archinstall.arguments.get('silent'):
- prompt = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?'))
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
- if choice.value == Menu.yes():
- try:
- installation.drop_to_shell()
- except:
- pass
-
- # For support reasons, we'll log the disk layout post installation (crash or no crash)
- archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
-
-
-if archinstall.arguments.get('skip-mirror-check', False) is False and archinstall.check_mirror_reachable() is False:
- log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None))
- archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red")
- exit(1)
-
-if not archinstall.arguments.get('silent'):
- ask_user_questions()
-
-config_output = ConfigurationOutput(archinstall.arguments)
-if not archinstall.arguments.get('silent'):
- config_output.show()
-config_output.save()
-
-if archinstall.arguments.get('dry_run'):
- exit(0)
-
-if not archinstall.arguments.get('silent'):
- input(str(_('Press Enter to continue.')))
-
-archinstall.configuration_sanity_check()
-perform_filesystem_operations()
-perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt'))
diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py
new file mode 100644
index 00000000..4513b6f2
--- /dev/null
+++ b/examples/interactive_installation.py
@@ -0,0 +1,206 @@
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Optional
+
+import archinstall
+from archinstall import Installer
+from archinstall import profile
+from archinstall import SysInfo
+from archinstall import disk
+from archinstall import menu
+from archinstall import models
+from archinstall import locale
+from archinstall import info, debug
+
+if TYPE_CHECKING:
+ _: Any
+
+
+def ask_user_questions():
+ global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments)
+
+ global_menu.enable('archinstall-language')
+
+ # Set which region to download packages from during the installation
+ global_menu.enable('mirror_config')
+
+ global_menu.enable('locale_config')
+
+ global_menu.enable('disk_config', mandatory=True)
+
+ # Specify disk encryption options
+ global_menu.enable('disk_encryption')
+
+ # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB)
+ global_menu.enable('bootloader')
+
+ global_menu.enable('swap')
+
+ # Get the hostname for the machine
+ global_menu.enable('hostname')
+
+ # Ask for a root password (optional, but triggers requirement for super-user if skipped)
+ global_menu.enable('!root-password', mandatory=True)
+
+ global_menu.enable('!users', mandatory=True)
+
+ # Ask for archinstall-specific profiles_bck (such as desktop environments etc)
+ global_menu.enable('profile_config')
+
+ # Ask about audio server selection if one is not already set
+ global_menu.enable('audio_config')
+
+ # Ask for preferred kernel:
+ global_menu.enable('kernels', mandatory=True)
+
+ global_menu.enable('packages')
+
+ if archinstall.arguments.get('advanced', False):
+ # Enable parallel downloads
+ global_menu.enable('parallel downloads')
+
+ # Ask or Call the helper function that asks the user to optionally configure a network.
+ global_menu.enable('network_config')
+
+ global_menu.enable('timezone')
+
+ global_menu.enable('ntp')
+
+ global_menu.enable('additional-repositories')
+
+ global_menu.enable('__separator__')
+
+ global_menu.enable('save_config')
+ global_menu.enable('install')
+ global_menu.enable('abort')
+
+ global_menu.run()
+
+
+def perform_installation(mountpoint: Path):
+ """
+ Performs the installation steps on a block device.
+ Only requirement is that the block devices are
+ formatted and setup prior to entering this function.
+ """
+ info('Starting installation...')
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+
+ # Retrieve list of additional repositories and set boolean values appropriately
+ enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', [])
+ enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', [])
+ locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ # Mount all the drives to the desired mountpoint
+ if disk_config.config_type != disk.DiskLayoutType.Pre_mount:
+ installation.mount_ordered_layout()
+
+ installation.sanity_check()
+
+ if disk_config.config_type != disk.DiskLayoutType.Pre_mount:
+ if disk_encryption and disk_encryption.encryption_type != disk.EncryptionType.NoEncryption:
+ # generate encryption key files for the mounted luks devices
+ installation.generate_key_files()
+
+ if mirror_config := archinstall.arguments.get('mirror_config', None):
+ installation.set_mirrors(mirror_config)
+
+ installation.minimal_installation(
+ testing=enable_testing,
+ multilib=enable_multilib,
+ hostname=archinstall.arguments.get('hostname', 'archlinux'),
+ locale_config=locale_config
+ )
+
+ if mirror_config := archinstall.arguments.get('mirror_config', None):
+ installation.set_mirrors(mirror_config, on_target=True)
+
+ if archinstall.arguments.get('swap'):
+ installation.setup_swap('zram')
+
+ if archinstall.arguments.get("bootloader") == models.Bootloader.Grub and SysInfo.has_uefi():
+ installation.add_additional_packages("grub")
+
+ installation.add_bootloader(archinstall.arguments["bootloader"])
+
+ # If user selected to copy the current ISO network configuration
+ # Perform a copy of the config
+ network_config = archinstall.arguments.get('network_config', None)
+
+ if network_config:
+ network_config.install_network_config(
+ installation,
+ archinstall.arguments.get('profile_config', None)
+ )
+
+ if users := archinstall.arguments.get('!users', None):
+ installation.create_users(users)
+
+ audio_config: Optional[models.AudioConfiguration] = archinstall.arguments.get('audio_config', None)
+ if audio_config:
+ audio_config.install_audio_config(installation)
+ else:
+ info("No audio server will be installed")
+
+ if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '':
+ installation.add_additional_packages(archinstall.arguments.get('packages', []))
+
+ if profile_config := archinstall.arguments.get('profile_config', None):
+ profile.profile_handler.install_profile_config(installation, profile_config)
+
+ if timezone := archinstall.arguments.get('timezone', None):
+ installation.set_timezone(timezone)
+
+ if archinstall.arguments.get('ntp', False):
+ installation.activate_time_synchronization()
+
+ if archinstall.accessibility_tools_in_use():
+ installation.enable_espeakup()
+
+ if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
+ installation.user_set_pw('root', root_pw)
+
+ if profile_config := archinstall.arguments.get('profile_config', None):
+ profile_config.profile.post_install(installation)
+
+ # If the user provided a list of services to be enabled, pass the list to the enable_service function.
+ # Note that while it's called enable_service, it can actually take a list of services and iterate it.
+ if archinstall.arguments.get('services', None):
+ installation.enable_service(archinstall.arguments.get('services', []))
+
+ # If the user provided custom commands to be run post-installation, execute them now.
+ if archinstall.arguments.get('custom-commands', None):
+ archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation)
+
+ installation.genfstab()
+
+ info("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation")
+
+ if not archinstall.arguments.get('silent'):
+ prompt = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?'))
+ choice = menu.Menu(prompt, menu.Menu.yes_no(), default_option=menu.Menu.yes()).run()
+ if choice.value == menu.Menu.yes():
+ try:
+ installation.drop_to_shell()
+ except Exception:
+ pass
+
+ debug(f"Disk states after installing: {disk.disk_layouts()}")
+
+
+ask_user_questions()
+
+fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+)
+
+fs_handler.perform_filesystem_operations()
+
+perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')))
diff --git a/examples/mac_address_installation.py b/examples/mac_address_installation.py
new file mode 100644
index 00000000..74a123c7
--- /dev/null
+++ b/examples/mac_address_installation.py
@@ -0,0 +1,18 @@
+import time
+
+import archinstall
+from archinstall import profile, info
+
+for _profile in profile.profile_handler.get_mac_addr_profiles():
+ # Tailored means it's a match for this machine
+ # based on it's MAC address (or some other criteria
+ # that fits the requirements for this machine specifically).
+ info(f'Found a tailored profile for this machine called: "{_profile.name}"')
+
+ print('Starting install in:')
+ for i in range(10, 0, -1):
+ print(f'{i}...')
+ time.sleep(1)
+
+ install_session = archinstall.storage['installation_session']
+ _profile.install(install_session)
diff --git a/examples/minimal.py b/examples/minimal.py
deleted file mode 100644
index 8b4c847f..00000000
--- a/examples/minimal.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import archinstall
-
-# Select a harddrive and a disk password
-from archinstall import User
-
-archinstall.log("Minimal only supports:")
-archinstall.log(" * Being installed to a single disk")
-
-if archinstall.arguments.get('help', None):
- archinstall.log(" - Optional disk encryption via --!encryption-password=<password>")
- archinstall.log(" - Optional filesystem type via --filesystem=<fs type>")
- archinstall.log(" - Optional systemd network via --network")
-
-archinstall.arguments['harddrive'] = archinstall.select_disk(archinstall.all_blockdevices())
-
-
-def install_on(mountpoint):
- # We kick off the installer by telling it where the
- with archinstall.Installer(mountpoint) as installation:
- # Strap in the base system, add a boot loader and configure
- # some other minor details as specified by this profile and user.
- if installation.minimal_installation():
- installation.set_hostname('minimal-arch')
- installation.add_bootloader()
-
- # Optionally enable networking:
- if archinstall.arguments.get('network', None):
- installation.copy_iso_network_config(enable_services=True)
-
- installation.add_additional_packages(['nano', 'wget', 'git'])
- installation.install_profile('minimal')
-
- user = User('devel', 'devel', False)
- installation.create_users(user)
-
- # Once this is done, we output some useful information to the user
- # And the installation is complete.
- archinstall.log("There are two new accounts in your installation after reboot:")
- archinstall.log(" * root (password: airoot)")
- archinstall.log(" * devel (password: devel)")
-
-
-if archinstall.arguments['harddrive']:
- archinstall.arguments['harddrive'].keep_partitions = False
-
- print(f" ! Formatting {archinstall.arguments['harddrive']} in ", end='')
- archinstall.do_countdown()
-
- # First, we configure the basic filesystem layout
- with archinstall.Filesystem(archinstall.arguments['harddrive'], archinstall.GPT) as fs:
- # We use the entire disk instead of setting up partitions on your own
- if archinstall.arguments['harddrive'].keep_partitions is False:
- fs.use_entire_disk(root_filesystem_type=archinstall.arguments.get('filesystem', 'btrfs'))
-
- boot = fs.find_partition('/boot')
- root = fs.find_partition('/')
-
- boot.format('fat32')
-
- # We encrypt the root partition if we got a password to do so with,
- # Otherwise we just skip straight to formatting and installation
- if archinstall.arguments.get('!encryption-password', None):
- root.encrypted = True
- root.encrypt(password=archinstall.arguments.get('!encryption-password', None))
-
- with archinstall.luks2(root, 'luksloop', archinstall.arguments.get('!encryption-password', None)) as unlocked_root:
- unlocked_root.format(root.filesystem)
- unlocked_root.mount('/mnt')
- else:
- root.format(root.filesystem)
- root.mount('/mnt')
-
- boot.mount('/mnt/boot')
-
-install_on('/mnt')
diff --git a/examples/minimal_installation.py b/examples/minimal_installation.py
new file mode 100644
index 00000000..c91a5d46
--- /dev/null
+++ b/examples/minimal_installation.py
@@ -0,0 +1,86 @@
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, List
+
+import archinstall
+from archinstall import disk
+from archinstall import Installer
+from archinstall import profile
+from archinstall import models
+from archinstall import interactions
+from archinstall.default_profiles.minimal import MinimalProfile
+
+if TYPE_CHECKING:
+ _: Any
+
+
+def perform_installation(mountpoint: Path):
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ # Strap in the base system, add a boot loader and configure
+ # some other minor details as specified by this profile and user.
+ if installation.minimal_installation():
+ installation.set_hostname('minimal-arch')
+ installation.add_bootloader(models.Bootloader.Systemd)
+
+ # Optionally enable networking:
+ if archinstall.arguments.get('network', None):
+ installation.copy_iso_network_config(enable_services=True)
+
+ installation.add_additional_packages(['nano', 'wget', 'git'])
+
+ profile_config = profile.ProfileConfiguration(MinimalProfile())
+ profile.profile_handler.install_profile_config(installation, profile_config)
+
+ user = models.User('devel', 'devel', False)
+ installation.create_users(user)
+
+
+def prompt_disk_layout():
+ fs_type = None
+ if filesystem := archinstall.arguments.get('filesystem', None):
+ fs_type = disk.FilesystemType(filesystem)
+
+ devices = interactions.select_devices()
+ modifications = interactions.suggest_single_disk_layout(devices[0], filesystem_type=fs_type)
+
+ archinstall.arguments['disk_config'] = disk.DiskLayoutConfiguration(
+ config_type=disk.DiskLayoutType.Default,
+ device_modifications=[modifications]
+ )
+
+
+def parse_disk_encryption():
+ if enc_password := archinstall.arguments.get('!encryption-password', None):
+ modification: List[disk.DeviceModification] = archinstall.arguments['disk_config']
+ partitions: List[disk.PartitionModification] = []
+
+ # encrypt all partitions except the /boot
+ for mod in modification:
+ partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions))
+
+ archinstall.arguments['disk_encryption'] = disk.DiskEncryption(
+ encryption_type=disk.EncryptionType.Luks,
+ encryption_password=enc_password,
+ partitions=partitions
+ )
+
+
+prompt_disk_layout()
+parse_disk_encryption()
+
+fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+)
+
+fs_handler.perform_filesystem_operations()
+
+mount_point = Path('/mnt')
+perform_installation(mount_point)
diff --git a/examples/only_hd.py b/examples/only_hd.py
deleted file mode 100644
index e3d18f0a..00000000
--- a/examples/only_hd.py
+++ /dev/null
@@ -1,151 +0,0 @@
-
-import logging
-import os
-import pathlib
-
-import archinstall
-from archinstall import ConfigurationOutput
-
-
-class OnlyHDMenu(archinstall.GlobalMenu):
- def _setup_selection_menu_options(self):
- super()._setup_selection_menu_options()
- options_list = []
- mandatory_list = []
- options_list = ['harddrives', 'disk_layouts', 'disk_encryption','swap']
- mandatory_list = ['harddrives']
- options_list.extend(['save_config','install','abort'])
-
- for entry in self._menu_options:
- if entry in options_list:
- # for not lineal executions, only self.option(entry).set_enabled and set_mandatory are necessary
- if entry in mandatory_list:
- self.enable(entry,mandatory=True)
- else:
- self.enable(entry)
- else:
- self.option(entry).set_enabled(False)
- self._update_install_text()
-
- def mandatory_lacking(self) -> [int, list]:
- mandatory_fields = []
- mandatory_waiting = 0
- for field in self._menu_options:
- option = self._menu_options[field]
- if option.is_mandatory():
- if not option.has_selection():
- mandatory_waiting += 1
- mandatory_fields += [field,]
- return mandatory_fields, mandatory_waiting
-
- def _missing_configs(self):
- """ overloaded method """
- def check(s):
- return self.option(s).has_selection()
-
- missing, missing_cnt = self.mandatory_lacking()
- if check('harddrives'):
- if not self.option('harddrives').is_empty() and not check('disk_layouts'):
- missing_cnt += 1
- missing += ['disk_layout']
- return missing
-
-def ask_user_questions():
- """
- First, we'll ask the user for a bunch of user input.
- Not until we're satisfied with what we want to install
- will we continue with the actual installation steps.
- """
- with OnlyHDMenu(data_store=archinstall.arguments) as menu:
- # We select the execution language separated
- menu.exec_option('archinstall-language')
- menu.option('archinstall-language').set_enabled(False)
- menu.run()
-
-def perform_disk_operations():
- """
- Issue a final warning before we continue with something un-revertable.
- We mention the drive one last time, and count from 5 to 0.
- """
- if archinstall.arguments.get('harddrives', None):
- print(f" ! Formatting {archinstall.arguments['harddrives']} in ", end='')
- archinstall.do_countdown()
- """
- Setup the blockdevice, filesystem (and optionally encryption).
- Once that's done, we'll hand over to perform_installation()
- """
- mode = archinstall.GPT
- if archinstall.has_uefi() is False:
- mode = archinstall.MBR
-
- for drive in archinstall.arguments.get('harddrives', []):
- if archinstall.arguments.get('disk_layouts', {}).get(drive.path):
- with archinstall.Filesystem(drive, mode) as fs:
- fs.load_layout(archinstall.arguments['disk_layouts'][drive.path])
-
-def perform_installation(mountpoint):
- """
- Performs the installation steps on a block device.
- Only requirement is that the block devices are
- formatted and setup prior to entering this function.
- """
- with archinstall.Installer(mountpoint, kernels=None) as installation:
- # Mount all the drives to the desired mountpoint
- # This *can* be done outside of the installation, but the installer can deal with it.
- if archinstall.arguments.get('disk_layouts'):
- installation.mount_ordered_layout(archinstall.arguments['disk_layouts'])
-
- # Placing /boot check during installation because this will catch both re-use and wipe scenarios.
- for partition in installation.partitions:
- if partition.mountpoint == installation.target + '/boot':
- if partition.size <= 0.25: # in GB
- raise archinstall.DiskError(f"The selected /boot partition in use is not large enough to properly install a boot loader. Please resize it to at least 256MB and re-run the installation.")
- # to generate a fstab directory holder. Avoids an error on exit and at the same time checks the procedure
- target = pathlib.Path(f"{mountpoint}/etc/fstab")
- if not target.parent.exists():
- target.parent.mkdir(parents=True)
-
- # For support reasons, we'll log the disk layout post installation (crash or no crash)
- archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
-
-def log_execution_environment():
- # Log various information about hardware before starting the installation. This might assist in troubleshooting
- archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG)
- archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG)
- archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG)
- archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG)
- archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG)
-
- # For support reasons, we'll log the disk layout pre installation to match against post-installation layout
- archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
-
-
-if archinstall.arguments.get('help'):
- print("See `man archinstall` for help.")
- exit(0)
-if os.getuid() != 0:
- print("Archinstall requires root privileges to run. See --help for more.")
- exit(1)
-
-log_execution_environment()
-
-if not archinstall.check_mirror_reachable():
- log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None))
- archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red")
- exit(1)
-
-if not archinstall.arguments.get('silent'):
- ask_user_questions()
-
-config_output = ConfigurationOutput(archinstall.arguments)
-if not archinstall.arguments.get('silent'):
- config_output.show()
-config_output.save()
-
-if archinstall.arguments.get('dry_run'):
- exit(0)
-if not archinstall.arguments.get('silent'):
- input('Press Enter to continue.')
-
-perform_disk_operations()
-perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt'))
diff --git a/examples/only_hd_installation.py b/examples/only_hd_installation.py
new file mode 100644
index 00000000..075bde20
--- /dev/null
+++ b/examples/only_hd_installation.py
@@ -0,0 +1,61 @@
+from pathlib import Path
+
+import archinstall
+from archinstall import Installer, disk, debug
+
+
+def ask_user_questions():
+ global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments)
+
+ global_menu.enable('archinstall-language')
+
+ global_menu.enable('disk_config', mandatory=True)
+ global_menu.enable('disk_encryption')
+ global_menu.enable('swap')
+
+ global_menu.enable('save_config')
+ global_menu.enable('install')
+ global_menu.enable('abort')
+
+ global_menu.run()
+
+
+def perform_installation(mountpoint: Path):
+ """
+ Performs the installation steps on a block device.
+ Only requirement is that the block devices are
+ formatted and setup prior to entering this function.
+ """
+ disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config']
+ disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None)
+
+ with Installer(
+ mountpoint,
+ disk_config,
+ disk_encryption=disk_encryption,
+ kernels=archinstall.arguments.get('kernels', ['linux'])
+ ) as installation:
+ # Mount all the drives to the desired mountpoint
+ # This *can* be done outside of the installation, but the installer can deal with it.
+ if archinstall.arguments.get('disk_config'):
+ installation.mount_ordered_layout()
+
+ # to generate a fstab directory holder. Avoids an error on exit and at the same time checks the procedure
+ target = Path(f"{mountpoint}/etc/fstab")
+ if not target.parent.exists():
+ target.parent.mkdir(parents=True)
+
+ # For support reasons, we'll log the disk layout post installation (crash or no crash)
+ debug(f"Disk states after installing: {disk.disk_layouts()}")
+
+
+ask_user_questions()
+
+fs_handler = disk.FilesystemHandler(
+ archinstall.arguments['disk_config'],
+ archinstall.arguments.get('disk_encryption', None)
+)
+
+fs_handler.perform_filesystem_operations()
+
+perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')))
diff --git a/examples/swiss.py b/examples/swiss.py
deleted file mode 100644
index 442281de..00000000
--- a/examples/swiss.py
+++ /dev/null
@@ -1,526 +0,0 @@
-"""
-
-Script swiss (army knife)
-Designed to make different workflows for the installation process. Which is controlled by the argument --mode
-mode full guides the full process of installation
-mode only_hd only proceeds to the creation of the disk infraestructure (partition, mount points, encryption)
-mode only_os processes only the installation of Archlinux and software at --mountpoint (or /mnt/archinstall)
-mode minimal (still not implemented)
-mode lineal. Instead of a menu, shows a sequence of selection screens (eq. to the old mode for guided.py)
-
-When using the argument --advanced. an additional menu for several special parameters needed during installation appears
-
-This script respects the --dry_run argument
-
-"""
-import logging
-import os
-import time
-import pathlib
-from typing import TYPE_CHECKING, Any
-
-import archinstall
-from archinstall import ConfigurationOutput, NetworkConfigurationHandler, Menu
-
-if TYPE_CHECKING:
- _: Any
-
-if archinstall.arguments.get('help'):
- print("See `man archinstall` for help.")
- exit(0)
-if os.getuid() != 0:
- print("Archinstall requires root privileges to run. See --help for more.")
- exit(1)
-
-"""
-particular routines to SetupMenu
-TODO exec con return parameter
-"""
-def select_activate_NTP():
- prompt = "Would you like to use automatic time synchronization (NTP) with the default time servers? [Y/n]: "
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
- if choice == Menu.yes():
- return True
- else:
- return False
-
-
-def select_mode():
- return archinstall.generic_select(['full','only_hd','only_os','minimal','lineal'],
- 'Select one execution mode',
- default=archinstall.arguments.get('mode','full'))
-
-
-"""
-following functions will be at locale_helpers, so they will have to be called prefixed by archinstall
-"""
-def get_locale_mode_text(mode):
- if mode == 'LC_ALL':
- mode_text = "general (LC_ALL)"
- elif mode == "LC_CTYPE":
- mode_text = "Character set"
- elif mode == "LC_NUMERIC":
- mode_text = "Numeric values"
- elif mode == "LC_TIME":
- mode_text = "Time Values"
- elif mode == "LC_COLLATE":
- mode_text = "sort order"
- elif mode == "LC_MESSAGES":
- mode_text = "text messages"
- else:
- mode_text = "Unassigned"
- return mode_text
-
-def reset_cmd_locale():
- """ sets the cmd_locale to its saved default """
- archinstall.storage['CMD_LOCALE'] = archinstall.storage.get('CMD_LOCALE_DEFAULT',{})
-
-def unset_cmd_locale():
- """ archinstall will use the execution environment default """
- archinstall.storage['CMD_LOCALE'] = {}
-
-def set_cmd_locale(general :str = None,
- charset :str = 'C',
- numbers :str = 'C',
- time :str = 'C',
- collate :str = 'C',
- messages :str = 'C'):
- """
- Set the cmd locale.
- If the parameter general is specified, it takes precedence over the rest (might as well not exist)
- The rest define some specific settings above the installed default language. If anyone of this parameters is none means the installation default
- """
- installed_locales = list_installed_locales()
- result = {}
- if general:
- if general in installed_locales:
- archinstall.storage['CMD_LOCALE'] = {'LC_ALL':general}
- else:
- archinstall.log(f"{get_locale_mode_text('LC_ALL')} {general} is not installed. Defaulting to C",fg="yellow",level=logging.WARNING)
- return
-
- if numbers:
- if numbers in installed_locales:
- result["LC_NUMERIC"] = numbers
- else:
- archinstall.log(f"{get_locale_mode_text('LC_NUMERIC')} {numbers} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if charset:
- if charset in installed_locales:
- result["LC_CTYPE"] = charset
- else:
- archinstall.log(f"{get_locale_mode_text('LC_CTYPE')} {charset} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if time:
- if time in installed_locales:
- result["LC_TIME"] = time
- else:
- archinstall.log(f"{get_locale_mode_text('LC_TIME')} {time} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if collate:
- if collate in installed_locales:
- result["LC_COLLATE"] = collate
- else:
- archinstall.log(f"{get_locale_mode_text('LC_COLLATE')} {collate} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- if messages:
- if messages in installed_locales:
- result["LC_MESSAGES"] = messages
- else:
- archinstall.log(f"{get_locale_mode_text('LC_MESSAGES')} {messages} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING)
- archinstall.storage['CMD_LOCALE'] = result
-
-def list_installed_locales() -> list[str]:
- lista = []
- for line in archinstall.SysCommand('locale -a'):
- lista.append(line.decode('UTF-8').strip())
- return lista
-
-
-"""
-end of locale helpers
-"""
-
-def select_installed_locale(mode):
- mode_text = get_locale_mode_text(mode)
- if mode == 'LC_ALL':
- texto = "Select the default execution locale \nIf none, you will be prompted for specific settings"
- else:
- texto = f"Select the {mode_text} ({mode}) execution locale \nIf none, you will get the installation default"
- return archinstall.generic_select([None] + list_installed_locales(),
- texto,
- allow_empty_input=True,
- default=archinstall.storage.get('CMD_LOCALE',{}).get(mode,'C'))
-
-
-"""
- _menus
-"""
-
-class SetupMenu(archinstall.AbstractMenu):
- def __init__(self,storage_area):
- super().__init__(data_store=storage_area)
-
- def _setup_selection_menu_options(self):
- self.set_option(
- 'archinstall-language',
- archinstall.Selector(
- _('Archinstall language'),
- lambda x: self._select_archinstall_language(x),
- display_func=lambda x: x.display_name,
- default=self.translation_handler.get_language_by_abbr('en'),
- enabled=True
- )
- )
-
- self.set_option(
- 'ntp',
- archinstall.Selector(
- 'Activate NTP',
- lambda x: select_activate_NTP(),
- default='Y',
- enabled=True
- )
- )
-
- self.set_option(
- 'mode',
- archinstall.Selector(
- 'Excution mode',
- lambda x : select_mode(),
- default='full',
- enabled=True)
- )
-
- for item in ['LC_ALL','LC_CTYPE','LC_NUMERIC','LC_TIME','LC_MESSAGES','LC_COLLATE']:
- self.set_option(item,
- archinstall.Selector(
- f'{get_locale_mode_text(item)} locale',
- lambda x,item=item: select_installed_locale(item), # the parameter is needed for the lambda in the loop
- enabled=True,
- dependencies_not=['LC_ALL'] if item != 'LC_ALL' else []))
- self.option('LC_ALL').set_enabled(True)
- self.set_option('continue',
- archinstall.Selector(
- 'Continue',
- exec_func=lambda n,v: True,
- enabled=True))
-
- def exit_callback(self):
- if self._data_store.get('ntp',False):
- archinstall.log("Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki.", fg="yellow")
- archinstall.SysCommand('timedatectl set-ntp true')
- if self._data_store.get('mode',None):
- archinstall.arguments['mode'] = self._data_store['mode']
- archinstall.log(f"Archinstall will execute under {archinstall.arguments['mode']} mode")
- if self._data_store.get('LC_ALL',None):
- archinstall.storage['CMD_LOCALE'] = {'LC_ALL':self._data_store['LC_ALL']}
- else:
- exec_locale = {}
- for item in ['LC_COLLATE','LC_CTYPE','LC_MESSAGES','LC_NUMERIC','LC_TIME']:
- if self._data_store.get(item,None):
- exec_locale[item] = self._data_store[item]
- archinstall.storage['CMD_LOCALE'] = exec_locale
- archinstall.log(f"Archinstall will execute with {archinstall.storage.get('CMD_LOCALE',None)} locale")
-
-class MyMenu(archinstall.GlobalMenu):
- def __init__(self,data_store=archinstall.arguments,mode='full'):
- self._execution_mode = mode
- super().__init__(data_store)
-
- def _setup_selection_menu_options(self):
- super()._setup_selection_menu_options()
- options_list = []
- mandatory_list = []
- if self._execution_mode in ('full','lineal'):
- options_list = ['keyboard-layout', 'mirror-region', 'harddrives', 'disk_layouts',
- 'disk_encryption','swap', 'bootloader', 'hostname', '!root-password',
- '!users', 'profile', 'audio', 'kernels', 'packages','additional-repositories','nic',
- 'timezone', 'ntp']
- if archinstall.arguments.get('advanced',False):
- options_list.extend(['sys-language','sys-encoding'])
- mandatory_list = ['harddrives','bootloader','hostname']
- elif self._execution_mode == 'only_hd':
- options_list = ['harddrives', 'disk_layouts', 'disk_encryption','swap']
- mandatory_list = ['harddrives']
- elif self._execution_mode == 'only_os':
- options_list = ['keyboard-layout', 'mirror-region','bootloader', 'hostname',
- '!root-password', '!users', 'profile', 'audio', 'kernels',
- 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp']
- mandatory_list = ['hostname']
- if archinstall.arguments.get('advanced',False):
- options_list.expand(['sys-language','sys-encoding'])
- elif self._execution_mode == 'minimal':
- pass
- else:
- archinstall.log(f"self._execution_mode {self._execution_mode} not supported")
- exit(1)
- if self._execution_mode != 'lineal':
- options_list.extend(['save_config','install','abort'])
- if not archinstall.arguments.get('advanced'):
- options_list.append('archinstall-language')
-
- for entry in self._menu_options:
- if entry in options_list:
- # for not lineal executions, only self.option(entry).set_enabled and set_mandatory are necessary
- if entry in mandatory_list:
- self.enable(entry,mandatory=True)
- else:
- self.enable(entry)
- else:
- self.option(entry).set_enabled(False)
- self._update_install_text()
-
- def post_callback(self,option=None,value=None):
- self._update_install_text(self._execution_mode)
-
- def _missing_configs(self,mode='full'):
- def check(s):
- return self.option(s).has_selection()
-
- def has_superuser() -> bool:
- users = self._menu_options['!users'].current_selection
- return any([u.sudo for u in users])
-
- _, missing = self.mandatory_overview()
- if mode in ('full','only_os') and (not check('!root-password') and not has_superuser()):
- missing += 1
- if mode in ('full', 'only_hd') and check('harddrives'):
- if not self.option('harddrives').is_empty() and not check('disk_layouts'):
- missing += 1
- return missing
-
- def _install_text(self,mode='full'):
- missing = self._missing_configs(mode)
- if missing > 0:
- return f'Instalation ({missing} config(s) missing)'
- return 'Install'
-
- def _update_install_text(self, mode='full'):
- text = self._install_text(mode)
- self.option('install').update_description(text)
-
-
-"""
-Installation general subroutines
-"""
-
-def get_current_status():
- # Log various information about hardware before starting the installation. This might assist in troubleshooting
- archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG)
- archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG)
- archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG)
- archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG)
- archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG)
-
- # For support reasons, we'll log the disk layout pre installation to match against post-installation layout
- archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
-
-def ask_user_questions(mode):
- """
- First, we'll ask the user for a bunch of user input.
- Not until we're satisfied with what we want to install
- will we continue with the actual installation steps.
- """
- if archinstall.arguments.get('advanced',None):
- # 3.9 syntax. former x = {**y,**z} or x.update(y)
- set_cmd_locale(charset='es_ES.utf8',collate='es_ES.utf8')
- setup_area = archinstall.storage.get('CMD_LOCALE',{}) | {}
- with SetupMenu(setup_area) as setup:
- if mode == 'lineal':
- for entry in setup.list_enabled_options():
- if entry in ('continue','abort'):
- continue
- if not setup.option(entry).enabled:
- continue
- setup.exec_option(entry)
- else:
- setup.run()
- archinstall.arguments['archinstall-language'] = setup_area.get('archinstall-language')
- else:
- archinstall.log("Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki.", fg="yellow")
- archinstall.SysCommand('timedatectl set-ntp true')
-
- with MyMenu(data_store=archinstall.arguments,mode=mode) as global_menu:
-
- if mode == 'lineal':
- for entry in global_menu.list_enabled_options():
- if entry in ('install','abort'):
- continue
- global_menu.exec_option(entry)
- archinstall.arguments[entry] = global_menu.option(entry).get_selection()
- else:
- global_menu.set_option('install',
- archinstall.Selector(
- global_menu._install_text(mode),
- exec_func=lambda n,v: True if global_menu._missing_configs(mode) == 0 else False,
- enabled=True))
-
- global_menu.run()
-
-def perform_filesystem_operations():
- """
- Issue a final warning before we continue with something un-revertable.
- We mention the drive one last time, and count from 5 to 0.
- """
-
- if archinstall.arguments.get('harddrives', None):
- print(f" ! Formatting {archinstall.arguments['harddrives']} in ", end='')
- archinstall.do_countdown()
-
- """
- Setup the blockdevice, filesystem (and optionally encryption).
- Once that's done, we'll hand over to perform_installation()
- """
-
- mode = archinstall.GPT
- if archinstall.has_uefi() is False:
- mode = archinstall.MBR
-
- for drive in archinstall.arguments.get('harddrives', []):
- if archinstall.arguments.get('disk_layouts', {}).get(drive.path):
- with archinstall.Filesystem(drive, mode) as fs:
- fs.load_layout(archinstall.arguments['disk_layouts'][drive.path])
-
-def disk_setup(installation):
- # Mount all the drives to the desired mountpoint
- # This *can* be done outside of the installation, but the installer can deal with it.
- if archinstall.arguments.get('disk_layouts'):
- installation.mount_ordered_layout(archinstall.arguments['disk_layouts'])
-
- # Placing /boot check during installation because this will catch both re-use and wipe scenarios.
- for partition in installation.partitions:
- if partition.mountpoint == installation.target + '/boot':
- if partition.size < 0.19: # ~200 MiB in GiB
- raise archinstall.DiskError(
- f"The selected /boot partition in use is not large enough to properly install a boot loader. Please resize it to at least 200MiB and re-run the installation.")
-
-def os_setup(installation):
- # if len(mirrors):
- # Certain services might be running that affects the system during installation.
- # Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist
- # We need to wait for it before we continue since we opted in to use a custom mirror/region.
- installation.log('Waiting for automatic mirror selection (reflector) to complete.', level=logging.INFO)
- while archinstall.service_state('reflector') not in ('dead', 'failed'):
- time.sleep(1)
- # Set mirrors used by pacstrap (outside of installation)
- if archinstall.arguments.get('mirror-region', None):
- archinstall.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium
- if installation.minimal_installation(
- hostname=archinstall.arguments['hostname'],
- locales=[f"{archinstall.arguments['sys-language']} {archinstall.arguments['sys-encoding'].upper()}"]):
- if archinstall.arguments['mirror-region'].get("mirrors", None) is not None:
- installation.set_mirrors(
- archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium
- if archinstall.arguments["bootloader"] == "grub-install" and archinstall.has_uefi():
- installation.add_additional_packages("grub")
- installation.add_bootloader(archinstall.arguments["bootloader"])
- if archinstall.arguments['swap']:
- installation.setup_swap('zram')
-
- network_config = archinstall.arguments.get('nic', None)
-
- if network_config:
- handler = NetworkConfigurationHandler(network_config)
- handler.config_installer(installation)
-
- if archinstall.arguments.get('audio', None) is not None:
- installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}",level=logging.INFO)
- if archinstall.arguments.get('audio', None) == 'pipewire':
- archinstall.Application(installation, 'pipewire').install()
- elif archinstall.arguments.get('audio', None) == 'pulseaudio':
- print('Installing pulseaudio ...')
- installation.add_additional_packages("pulseaudio")
- else:
- installation.log("No audio server will be installed.", level=logging.INFO)
-
- if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '':
- installation.add_additional_packages(archinstall.arguments.get('packages', None))
-
- if archinstall.arguments.get('profile', None):
- installation.install_profile(archinstall.arguments.get('profile', None))
-
- if users := archinstall.arguments.get('!users', None):
- installation.create_users(users)
-
- if timezone := archinstall.arguments.get('timezone', None):
- installation.set_timezone(timezone)
-
- if archinstall.arguments.get('ntp', False):
- installation.activate_time_syncronization()
-
- if archinstall.accessibility_tools_in_use():
- installation.enable_espeakup()
-
- if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
- installation.user_set_pw('root', root_pw)
-
- # This step must be after profile installs to allow profiles to install language pre-requisits.
- # After which, this step will set the language both for console and x11 if x11 was installed for instance.
- installation.set_keyboard_language(archinstall.arguments['keyboard-layout'])
-
- if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_post_install():
- with archinstall.arguments['profile'].load_instructions(
- namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported:
- if not imported._post_install():
- archinstall.log(' * Profile\'s post configuration requirements was not fulfilled.', fg='red')
- exit(1)
-
- # If the user provided a list of services to be enabled, pass the list to the enable_service function.
- # Note that while it's called enable_service, it can actually take a list of services and iterate it.
- if archinstall.arguments.get('services', None):
- installation.enable_service(*archinstall.arguments['services'])
-
- # If the user provided custom commands to be run post-installation, execute them now.
- if archinstall.arguments.get('custom-commands', None):
- archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation)
-
-
-def perform_installation(mountpoint, mode):
- """
- Performs the installation steps on a block device.
- Only requirement is that the block devices are
- formatted and setup prior to entering this function.
- """
- with archinstall.Installer(mountpoint, kernels=archinstall.arguments.get('kernels', ['linux'])) as installation:
- if mode in ('full','only_hd'):
- disk_setup(installation)
- if mode == 'only_hd':
- target = pathlib.Path(f"{mountpoint}/etc/fstab")
- if not target.parent.exists():
- target.parent.mkdir(parents=True)
-
- if mode in ('full','only_os'):
- os_setup(installation)
- installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow")
- if not archinstall.arguments.get('silent'):
- prompt = 'Would you like to chroot into the newly created installation and perform post-installation configuration?'
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
- if choice == Menu.yes():
- try:
- installation.drop_to_shell()
- except:
- pass
-
- # For support reasons, we'll log the disk layout post installation (crash or no crash)
- archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
-
-
-if not archinstall.check_mirror_reachable():
- log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None))
- archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red")
- exit(1)
-
-mode = archinstall.arguments.get('mode', 'full').lower()
-if not archinstall.arguments.get('silent'):
- ask_user_questions(mode)
-
-config_output = ConfigurationOutput(archinstall.arguments)
-if not archinstall.arguments.get('silent'):
- config_output.show()
-config_output.save()
-
-if archinstall.arguments.get('dry_run'):
- exit(0)
-if not archinstall.arguments.get('silent'):
- input('Press Enter to continue.')
-
-if mode in ('full','only_hd'):
- perform_filesystem_operations()
-perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt'), mode)
diff --git a/examples/unattended.py b/examples/unattended.py
deleted file mode 100644
index f1ed4c94..00000000
--- a/examples/unattended.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import time
-
-import archinstall
-
-archinstall.storage['UPSTREAM_URL'] = 'https://archlinux.life/profiles'
-archinstall.storage['PROFILE_DB'] = 'index.json'
-
-for name, info in archinstall.list_profiles().items():
- # Tailored means it's a match for this machine
- # based on it's MAC address (or some other criteria
- # that fits the requirements for this machine specifically).
- if info['tailored']:
- print(f'Found a tailored profile for this machine called: "{name}".')
- print('Starting install in:')
- for i in range(10, 0, -1):
- print(f'{i}...')
- time.sleep(1)
-
- profile = archinstall.Profile(None, info['path'])
- profile.install()
- break
diff --git a/mypy-strict.ini b/mypy-strict.ini
new file mode 100644
index 00000000..c670bbaa
--- /dev/null
+++ b/mypy-strict.ini
@@ -0,0 +1,102 @@
+[mypy]
+python_version = 3.10
+follow_imports = silent
+exclude = (?x)(
+ | ^archinstall/lib/configuration\.py$
+ | ^archinstall/lib/disk/btrfs/btrfssubvolumeinfo\.py$
+ | ^archinstall/lib/disk/helpers\.py$
+ | ^archinstall/lib/hsm/fido\.py$
+ | ^archinstall/lib/menu/list_manager\.py$
+ | ^archinstall/lib/menu/menu\.py$
+ | ^archinstall/lib/menu/simple_menu\.py$
+ | ^archinstall/lib/menu/text_input\.py$
+ | ^archinstall/lib/models/dataclasses\.py$
+ | ^archinstall/lib/models/network_configuration\.py$
+ | ^archinstall/lib/models/password_strength\.py$
+ | ^archinstall/lib/models/pydantic\.py$
+ | ^archinstall/lib/models/subvolume\.py$
+ | ^archinstall/lib/models/users\.py$
+ | ^archinstall/lib/output\.py$
+ | ^archinstall/lib/plugins\.py$
+ | ^archinstall/examples/guided\.py$
+ | ^archinstall/examples/minimal\.py$
+ | ^archinstall/examples/only_hd\.py$
+ | ^archinstall/examples/swiss\.py$
+ | ^archinstall/__init__\.py$
+ | ^archinstall/lib/disk/blockdevice\.py$
+ | ^archinstall/lib/disk/btrfs/btrfs_helpers\.py$
+ | ^archinstall/lib/disk/btrfs/btrfspartition\.py$
+ | ^archinstall/lib/disk/dmcryptdev\.py$
+ | ^archinstall/lib/disk/filesystem\.py$
+ | ^archinstall/lib/disk/mapperdev\.py$
+ | ^archinstall/lib/disk/partition\.py$
+ | ^archinstall/lib/disk/user_guides\.py$
+ | ^archinstall/lib/general\.py$
+ | ^archinstall/lib/hardware\.py$
+ | ^archinstall/lib/installer\.py$
+ | ^archinstall/lib/locale_helpers\.py$
+ | ^archinstall/lib/luks\.py$
+ | ^archinstall/lib/menu/global_menu\.py$
+ | ^archinstall/lib/menu/selection_menu\.py$
+ | ^archinstall/lib/mirrors\.py$
+ | ^archinstall/lib/networking\.py$
+ | ^archinstall/lib/packages/packages\.py$
+ | ^archinstall/lib/pacman\.py$
+ | ^archinstall/lib/profiles\.py$
+ | ^archinstall/lib/systemd\.py$
+ | ^archinstall/lib/translation\.py$
+ | ^archinstall/lib/user_interaction/backwards_compatible_conf\.py$
+ | ^archinstall/lib/user_interaction/disk_conf\.py$
+ | ^archinstall/lib/user_interaction/general_conf\.py$
+ | ^archinstall/lib/user_interaction/locale_conf\.py$
+ | ^archinstall/lib/user_interaction/manage_users_conf\.py$
+ | ^archinstall/lib/user_interaction/network_conf\.py$
+ | ^archinstall/lib/user_interaction/partitioning_conf\.py$
+ | ^archinstall/lib/user_interaction/save_conf\.py$
+ | ^archinstall/lib/user_interaction/system_conf\.py$
+ | ^archinstall/lib/user_interaction/utils\.py$
+ | ^archinstall/profiles/applications/pipewire\.py$
+ | ^archinstall/profiles/awesome\.py$
+ | ^archinstall/profiles/bspwm\.py$
+ | ^archinstall/profiles/budgie\.py$
+ | ^archinstall/profiles/cinnamon\.py$
+ | ^archinstall/profiles/cutefish\.py$
+ | ^archinstall/profiles/deepin\.py$
+ | ^archinstall/profiles/desktop\.py$
+ | ^archinstall/profiles/enlightenment\.py$
+ | ^archinstall/profiles/gnome\.py$
+ | ^archinstall/profiles/i3\.py$
+ | ^archinstall/profiles/lxqt\.py$
+ | ^archinstall/profiles/mate\.py$
+ | ^archinstall/profiles/minimal\.py$
+ | ^archinstall/profiles/plasma\.py$
+ | ^archinstall/profiles/qtile\.py$
+ | ^archinstall/profiles/server\.py$
+ | ^archinstall/profiles/sway\.py$
+ | ^archinstall/profiles/xfce4\.py$
+ | ^archinstall/profiles/xorg\.py$
+ | ^profiles/applications/pipewire\.py$
+ | ^profiles/awesome\.py$
+ | ^profiles/bspwm\.py$
+ | ^profiles/budgie\.py$
+ | ^profiles/cinnamon\.py$
+ | ^profiles/cutefish\.py$
+ | ^profiles/deepin\.py$
+ | ^profiles/desktop\.py$
+ | ^profiles/enlightenment\.py$
+ | ^profiles/gnome\.py$
+ | ^profiles/i3\.py$
+ | ^profiles/lxqt\.py$
+ | ^profiles/mate\.py$
+ | ^profiles/minimal\.py$
+ | ^profiles/plasma\.py$
+ | ^profiles/qtile\.py$
+ | ^profiles/server\.py$
+ | ^profiles/sway\.py$
+ | ^profiles/xfce4\.py$
+ | ^profiles/xorg\.py$
+ | ^examples/guided\.py$
+ | ^examples/only_hd\.py$
+ | ^examples/minimal\.py$
+ | ^examples/swiss\.py$)
+files = archinstall/, profiles/, examples/
diff --git a/profiles/52-54-00-12-34-56.py b/profiles/52-54-00-12-34-56.py
deleted file mode 100644
index 3b074629..00000000
--- a/profiles/52-54-00-12-34-56.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import archinstall
-
-# import json
-# import urllib.request
-
-__packages__ = ['nano', 'wget', 'git']
-
-if __name__ == '52-54-00-12-34-56':
- awesome = archinstall.Application(archinstall.storage['installation_session'], 'postgresql')
- awesome.install()
-
-"""
-# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.)
-archinstall.sys_command(f'umount -R /mnt', suppress_errors=True)
-archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True)
-
-# Select a harddrive and a disk password
-harddrive = archinstall.all_blockdevices()['/dev/sda']
-disk_password = '1234'
-
-with archinstall.Filesystem(harddrive) as fs:
- # Use the entire disk instead of setting up partitions on your own
- fs.use_entire_disk('luks2')
-
- if harddrive.partition[1].size == '512M':
- raise OSError('Trying to encrypt the boot partition for Pete's sake..')
- harddrive.partition[0].format('fat32')
-
- with archinstall.luks2(harddrive.partition[1], 'luksloop', disk_password) as unlocked_device:
- unlocked_device.format('btrfs')
-
- with archinstall.Installer(
- unlocked_device,
- boot_partition=harddrive.partition[0],
- hostname="testmachine"
- ) as installation:
- if installation.minimal_installation():
- installation.add_bootloader()
-
- installation.add_additional_packages(__packages__)
- installation.install_profile('awesome')
-
- user = User('devel', 'devel', False)
- installation.create_users(user)
- installation.user_set_pw('root', 'toor')
-
- print(f'Submitting {archinstall.__version__}: success')
-
- conditions = {
- "project": "archinstall",
- "profile": "52-54-00-12-34-56",
- "status": "success",
- "version": archinstall.__version__
- }
- req = urllib.request.Request("https://api.archlinux.life/build/success",
- data=json.dumps(conditions).encode('utf8'),
- headers={'content-type': 'application/json'})
- try:
- urllib.request.urlopen(req, timeout=5)
- except:
- pass
-"""
diff --git a/profiles/applications/awesome.py b/profiles/applications/awesome.py
deleted file mode 100644
index 33526fd7..00000000
--- a/profiles/applications/awesome.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import archinstall
-
-__packages__ = [
- "awesome",
- "xorg-xrandr",
- "xterm",
- "feh",
- "slock",
- "terminus-font",
- "gnu-free-fonts",
- "ttf-liberation",
- "xsel",
-]
-
-archinstall.storage['installation_session'].install_profile('xorg')
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-with open(f"{archinstall.storage['installation_session'].target}/etc/X11/xinit/xinitrc", 'r') as xinitrc:
- xinitrc_data = xinitrc.read()
-
-for line in xinitrc_data.split('\n'):
- if "twm &" in line:
- xinitrc_data = xinitrc_data.replace(line, f"# {line}")
- if "xclock" in line:
- xinitrc_data = xinitrc_data.replace(line, f"# {line}")
- if "xterm" in line:
- xinitrc_data = xinitrc_data.replace(line, f"# {line}")
-
-xinitrc_data += '\n'
-xinitrc_data += 'exec awesome\n'
-
-with open(f"{archinstall.storage['installation_session'].target}/etc/X11/xinit/xinitrc", 'w') as xinitrc:
- xinitrc.write(xinitrc_data)
diff --git a/profiles/applications/cockpit.py b/profiles/applications/cockpit.py
deleted file mode 100644
index d8aa0fd1..00000000
--- a/profiles/applications/cockpit.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = [
- "cockpit",
- "udisks2",
- "packagekit",
-]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('cockpit.socket')
diff --git a/profiles/applications/docker.py b/profiles/applications/docker.py
deleted file mode 100644
index afbde1a5..00000000
--- a/profiles/applications/docker.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["docker"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('docker')
diff --git a/profiles/applications/httpd.py b/profiles/applications/httpd.py
deleted file mode 100644
index 23b3fefa..00000000
--- a/profiles/applications/httpd.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["apache"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('httpd')
diff --git a/profiles/applications/lighttpd.py b/profiles/applications/lighttpd.py
deleted file mode 100644
index 71158861..00000000
--- a/profiles/applications/lighttpd.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["lighttpd"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('lighttpd')
diff --git a/profiles/applications/mariadb.py b/profiles/applications/mariadb.py
deleted file mode 100644
index bdde18b5..00000000
--- a/profiles/applications/mariadb.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["mariadb"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].arch_chroot("mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql")
-
-archinstall.storage['installation_session'].enable_service('mariadb')
diff --git a/profiles/applications/nginx.py b/profiles/applications/nginx.py
deleted file mode 100644
index 6f63b15c..00000000
--- a/profiles/applications/nginx.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["nginx"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('nginx')
diff --git a/profiles/applications/pipewire.py b/profiles/applications/pipewire.py
deleted file mode 100644
index b6e79347..00000000
--- a/profiles/applications/pipewire.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import archinstall
-import logging
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["pipewire", "pipewire-alsa", "pipewire-jack", "pipewire-pulse", "gst-plugin-pipewire", "libpulse", "wireplumber"]
-
-archinstall.log('Installing pipewire', level=logging.INFO)
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-@archinstall.plugin
-def on_user_created(installation :archinstall.Installer, user :str):
- archinstall.log(f"Enabling pipewire-pulse for {user}", level=logging.INFO)
- installation.chroot('systemctl enable --user pipewire-pulse.service', run_as=user)
diff --git a/profiles/applications/postgresql.py b/profiles/applications/postgresql.py
deleted file mode 100644
index 80ad4b66..00000000
--- a/profiles/applications/postgresql.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["postgresql"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].arch_chroot("initdb -D /var/lib/postgres/data", run_as='postgres')
-
-archinstall.storage['installation_session'].enable_service('postgresql')
diff --git a/profiles/applications/sshd.py b/profiles/applications/sshd.py
deleted file mode 100644
index 4199ecb0..00000000
--- a/profiles/applications/sshd.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import archinstall
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["openssh"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('sshd')
diff --git a/profiles/applications/tomcat.py b/profiles/applications/tomcat.py
deleted file mode 100644
index ae6d1c2a..00000000
--- a/profiles/applications/tomcat.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import archinstall
-
-# This is using Tomcat 10 as that is the latest release at the time of implementation.
-# This should probably be updated to use newer releases as they come out.
-
-# Define the package list in order for lib to source
-# which packages will be installed by this profile
-__packages__ = ["tomcat10"]
-
-archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
-archinstall.storage['installation_session'].enable_service('tomcat10')
diff --git a/profiles/awesome.py b/profiles/awesome.py
deleted file mode 100644
index 11c8de3b..00000000
--- a/profiles/awesome.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# A desktop environment using "Awesome" window manager.
-
-import archinstall
-
-is_top_level_profile = False
-
-# New way of defining packages for a profile, which is iterable and can be used out side
-# of the profile to get a list of "what packages will be installed".
-__packages__ = [
- "alacritty",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # Awesome WM requires that xorg is installed
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("awesome", "/somewhere/awesome.py")
-# or through conventional import awesome
-if __name__ == 'awesome':
- # Install the application awesome from the template under /applications/
- awesome = archinstall.Application(archinstall.storage['installation_session'], 'awesome')
- awesome.install()
-
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead.
- with open(f"{archinstall.storage['installation_session'].target}/etc/xdg/awesome/rc.lua", 'r') as fh:
- awesome_lua = fh.read()
-
- # Replace xterm with alacritty for a smoother experience.
- awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"')
-
- with open(f"{archinstall.storage['installation_session'].target}/etc/xdg/awesome/rc.lua", 'w') as fh:
- fh.write(awesome_lua)
-
- # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config) \ No newline at end of file
diff --git a/profiles/bspwm.py b/profiles/bspwm.py
deleted file mode 100644
index 0fb67ad6..00000000
--- a/profiles/bspwm.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# A desktop environment using the bspwm window manager.
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- 'bspwm',
- 'sxhkd',
- 'dmenu',
- 'xdo',
- 'rxvt-unicode',
- 'lightdm',
- 'lightdm-gtk-greeter',
-]
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # bspwm requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("bspwm", "/somewhere/bspwm.py")
-# or through conventional import bspwm
-if __name__ == 'bspwm':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
- # Install bspwm packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
- # Set up LightDM for login
- archinstall.storage['installation_session'].enable_service('lightdm')
diff --git a/profiles/budgie.py b/profiles/budgie.py
deleted file mode 100644
index 33484680..00000000
--- a/profiles/budgie.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# A desktop environment using "budgie"
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "arc-gtk-theme",
- "budgie",
- "lightdm",
- "lightdm-gtk-greeter",
- "mate-terminal",
- "nemo",
- "papirus-icon-theme",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # budgie requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("budgie", "/somewhere/budgie.py")
-# or through conventional import budgie
-if __name__ == 'budgie':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the Budgie packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager
diff --git a/profiles/cinnamon.py b/profiles/cinnamon.py
deleted file mode 100644
index 0122677a..00000000
--- a/profiles/cinnamon.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# A desktop environment using "Cinnamon"
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "cinnamon",
- "system-config-printer",
- "gnome-keyring",
- "gnome-terminal",
- "blueberry",
- "metacity",
- "lightdm",
- "lightdm-gtk-greeter",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # Cinnamon requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("cinnamon", "/somewhere/cinnamon.py")
-# or through conventional import cinnamon
-if __name__ == 'cinnamon':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the Cinnamon packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager
diff --git a/profiles/cutefish.py b/profiles/cutefish.py
deleted file mode 100644
index 486fa389..00000000
--- a/profiles/cutefish.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# A desktop environment using "Cutefish"
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "cutefish",
- "noto-fonts",
- "sddm"
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # Cutefish requires a functional xorg installation.
- profile = archinstall.Profile(None, "xorg")
- with profile.load_instructions(namespace="xorg.py") as imported:
- if hasattr(imported, "_prep_function"):
- return imported._prep_function()
- else:
- print("Deprecated (??): xorg profile has no _prep_function() anymore")
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("cutefish", "/somewhere/cutefish.py")
-# or through conventional import cutefish
-if __name__ == "cutefish":
- # Install dependency profiles
- archinstall.storage["installation_session"].install_profile("xorg")
-
- # Install the Cutefish packages
- archinstall.storage["installation_session"].add_additional_packages(__packages__)
-
- archinstall.storage["installation_session"].enable_service("sddm")
diff --git a/profiles/deepin.py b/profiles/deepin.py
deleted file mode 100644
index 8196bc4b..00000000
--- a/profiles/deepin.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# A desktop environment using "Deepin".
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "deepin",
- "deepin-terminal",
- "deepin-editor",
- "lightdm",
- "lightdm-deepin-greeter",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # Deepin requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("deepin", "/somewhere/deepin.py")
-# or through conventional import deepin
-if __name__ == 'deepin':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the Deepin packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # Enable autostart of Deepin for all users
- archinstall.storage['installation_session'].enable_service('lightdm')
diff --git a/profiles/desktop.py b/profiles/desktop.py
deleted file mode 100644
index e94d3505..00000000
--- a/profiles/desktop.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# A desktop environment selector.
-from typing import Any, TYPE_CHECKING
-
-import archinstall
-from archinstall import log, Menu
-from archinstall.lib.menu.menu import MenuSelectionType
-
-if TYPE_CHECKING:
- _: Any
-
-is_top_level_profile = True
-
-__description__ = str(_('Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway'))
-
-# New way of defining packages for a profile, which is iterable and can be used out side
-# of the profile to get a list of "what packages will be installed".
-__packages__ = [
- 'nano',
- 'vim',
- 'openssh',
- 'htop',
- 'wget',
- 'iwd',
- 'wireless_tools',
- 'wpa_supplicant',
- 'smartmontools',
- 'xdg-utils',
-]
-
-__supported__ = [
- 'gnome',
- 'kde',
- 'awesome',
- 'sway',
- 'cinnamon',
- 'xfce4',
- 'lxqt',
- 'i3',
- 'bspwm',
- 'budgie',
- 'mate',
- 'deepin',
- 'enlightenment',
- 'qtile'
-]
-
-
-def _prep_function(*args, **kwargs) -> bool:
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
- choice = Menu(str(_('Select your desired desktop environment')), __supported__).run()
-
- if choice.type_ != MenuSelectionType.Selection:
- return False
-
- if choice.value:
- # Temporarily store the selected desktop profile
- # in a session-safe location, since this module will get reloaded
- # the next time it gets executed.
- if not archinstall.storage.get('_desktop_profile', None):
- archinstall.storage['_desktop_profile'] = choice.value
- if not archinstall.arguments.get('desktop-environment', None):
- archinstall.arguments['desktop-environment'] = choice.value
- profile = archinstall.Profile(None, choice.value)
- # Loading the instructions with a custom namespace, ensures that a __name__ comparison is never triggered.
- with profile.load_instructions(namespace=f"{choice.value}.py") as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- log(f"Deprecated (??): {choice.value} profile has no _prep_function() anymore")
- exit(1)
-
- return False
-
-
-if __name__ == 'desktop':
- """
- This "profile" is a meta-profile.
- There are no desktop-specific steps, it simply routes
- the installer to whichever desktop environment/window manager was chosen.
-
- Maybe in the future, a network manager or similar things *could* be added here.
- We should honor that Arch Linux does not officially endorse a desktop-setup, nor is
- it trying to be a turn-key desktop distribution.
-
- There are plenty of desktop-turn-key-solutions based on Arch Linux,
- this is therefore just a helper to get started
- """
-
- # Install common packages for all desktop environments
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- archinstall.storage['installation_session'].install_profile(archinstall.storage['_desktop_profile'])
diff --git a/profiles/enlightenment.py b/profiles/enlightenment.py
deleted file mode 100644
index 3850fed0..00000000
--- a/profiles/enlightenment.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# A desktop environment using "Enlightenment".
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "enlightenment",
- "terminology",
- "lightdm",
- "lightdm-gtk-greeter",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # Enlightenment requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("enlightenment", "/somewhere/enlightenment.py")
-# or through conventional import enlightenment
-if __name__ == 'enlightenment':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the enlightenment packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # Enable autostart of enlightenment for all users
- archinstall.storage['installation_session'].enable_service('lightdm')
diff --git a/profiles/gnome.py b/profiles/gnome.py
deleted file mode 100644
index 5e3a8da6..00000000
--- a/profiles/gnome.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# A desktop environment using "Gnome"
-
-import archinstall
-
-is_top_level_profile = False
-
-# Note: GDM should be part of the gnome group, but adding it here for clarity
-__packages__ = [
- "gnome",
- "gnome-tweaks",
- "gdm"
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # Gnome optionally supports xorg, we'll install it since it also
- # includes graphic driver setups (this might change in the future)
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("gnome", "/somewhere/gnome.py")
-# or through conventional import gnome
-if __name__ == 'gnome':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the GNOME packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- archinstall.storage['installation_session'].enable_service('gdm') # Gnome Display Manager
-# We could also start it via xinitrc since we do have Xorg,
-# but for gnome that's deprecated and wayland is preferred.
diff --git a/profiles/i3.py b/profiles/i3.py
deleted file mode 100644
index d3e6e6b0..00000000
--- a/profiles/i3.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Common package for i3.
-
-import archinstall
-
-is_top_level_profile = False
-
-# New way of defining packages for a profile, which is iterable and can be used out side
-# of the profile to get a list of "what packages will be installed".
-__packages__ = [
- 'i3-wm'
- 'i3lock',
- 'i3status',
- 'i3blocks',
- 'xterm',
- 'lightdm-gtk-greeter',
- 'lightdm',
- 'dmenu',
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # i3 requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-if __name__ == 'i3':
- """
- This "profile" is a meta-profile.
- There are no desktop-specific steps, it simply routes
- the installer to whichever desktop environment/window manager was chosen.
-
- Maybe in the future, a network manager or similar things *could* be added here.
- We should honor that Arch Linux does not officially endorse a desktop-setup, nor is
- it trying to be a turn-key desktop distribution.
-
- There are plenty of desktop-turn-key-solutions based on Arch Linux,
- this is therefore just a helper to get started
- """
-
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the i3 packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # Enable autostart of lightdm for all users
- archinstall.storage['installation_session'].enable_service('lightdm')
diff --git a/profiles/kde.py b/profiles/kde.py
deleted file mode 100644
index d32bf31b..00000000
--- a/profiles/kde.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# A desktop environment using "KDE".
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "plasma-meta",
- "konsole",
- "kwrite",
- "dolphin",
- "ark",
- "sddm",
- "plasma-wayland-session",
- "egl-wayland"
-]
-
-
-# TODO: Remove hard dependency of bash (due to .bash_profile)
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # KDE requires a functioning Xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-"""
-def _post_install(*args, **kwargs):
- if "nvidia" in _gfx_driver_packages:
- print("Plasma Wayland has known compatibility issues with the proprietary Nvidia driver")
- print("After booting, you can choose between Wayland and Xorg using the drop-down menu")
- return True
-"""
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("kde", "/somewhere/kde.py")
-# or through conventional import kde
-if __name__ == 'kde':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the KDE packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # Enable autostart of KDE for all users
- archinstall.storage['installation_session'].enable_service('sddm')
diff --git a/profiles/lxqt.py b/profiles/lxqt.py
deleted file mode 100644
index 2419b4fa..00000000
--- a/profiles/lxqt.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# A desktop environment using "LXQt"
-
-import archinstall
-
-is_top_level_profile = False
-
-# NOTE: SDDM is the only officially supported greeter for LXQt, so unlike other DEs, lightdm is not used here.
-# LXQt works with lightdm, but since this is not supported, we will not default to this.
-# https://github.com/lxqt/lxqt/issues/795
-__packages__ = [
- "lxqt",
- "breeze-icons",
- "oxygen-icons",
- "xdg-utils",
- "ttf-freefont",
- "leafpad",
- "slock",
- "sddm",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # LXQt requires a functional xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("lxqt", "/somewhere/lxqt.py")
-# or through conventional import lxqt
-if __name__ == 'lxqt':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the LXQt packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # Enable autostart of LXQt for all users
- archinstall.storage['installation_session'].enable_service('sddm')
diff --git a/profiles/mate.py b/profiles/mate.py
deleted file mode 100644
index 94b91f81..00000000
--- a/profiles/mate.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# A desktop environment using "MATE"
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "mate",
- "mate-extra",
- "lightdm",
- "lightdm-gtk-greeter",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # MATE requires a functional xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("mate", "/somewhere/mate.py")
-# or through conventional import mate
-if __name__ == 'mate':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the MATE packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager
diff --git a/profiles/minimal.py b/profiles/minimal.py
deleted file mode 100644
index a412aa81..00000000
--- a/profiles/minimal.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Used to do a minimal install
-import archinstall
-
-is_top_level_profile = True
-
-__description__ = str(_('A very basic installation that allows you to customize Arch Linux as you see fit.'))
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. For minimal install,
- we don't need to do anything special here, but it
- needs to exist and return True.
- """
- archinstall.storage['profile_minimal'] = True
- return True # Do nothing and just return True
-
-
-if __name__ == 'minimal':
- """
- This "profile" is a meta-profile.
- It is used for a custom minimal installation, without any desktop-specific packages.
- """
diff --git a/profiles/qtile.py b/profiles/qtile.py
deleted file mode 100644
index ace13dcc..00000000
--- a/profiles/qtile.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# A desktop environment using "qtile" window manager with common packages.
-
-import archinstall
-
-is_top_level_profile = False
-
-# New way of defining packages for a profile, which is iterable and can be used out side
-# of the profile to get a list of "what packages will be installed".
-__packages__ = [
- 'qtile',
- 'alacritty',
- 'lightdm-gtk-greeter',
- 'lightdm',
-]
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # qtile optionally supports xorg, we'll install it since it also
- # includes graphic driver setups (this might change in the future)
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-if __name__ == 'qtile':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install packages for qtile
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- # Auto start lightdm for all users
- archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager
diff --git a/profiles/server.py b/profiles/server.py
deleted file mode 100644
index f3e32d26..00000000
--- a/profiles/server.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Used to select various server application profiles on top of a minimal installation.
-
-import logging
-from typing import Any, TYPE_CHECKING
-
-import archinstall
-from archinstall import Menu
-from archinstall.lib.menu.menu import MenuSelectionType
-
-if TYPE_CHECKING:
- _: Any
-
-is_top_level_profile = True
-
-__description__ = str(_('Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb'))
-
-available_servers = [
- "cockpit",
- "docker",
- "httpd",
- "lighttpd",
- "mariadb",
- "nginx",
- "postgresql",
- "sshd",
- "tomcat",
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further.
- """
- choice = Menu(str(_(
- 'Choose which servers to install, if none then a minimal installation will be done')),
- available_servers,
- preset_values=kwargs['servers'],
- multi=True
- ).run()
-
- if choice.type_ != MenuSelectionType.Selection:
- return False
-
- if choice.value:
- archinstall.storage['_selected_servers'] = choice.value
- return True
-
- return False
-
-
-if __name__ == 'server':
- """
- This "profile" is a meta-profile.
- """
- archinstall.log('Now installing the selected servers.', level=logging.INFO)
- archinstall.log(archinstall.storage['_selected_servers'], level=logging.DEBUG)
- for server in archinstall.storage['_selected_servers']:
- archinstall.log(f'Installing {server} ...', level=logging.INFO)
- app = archinstall.Application(archinstall.storage['installation_session'], server)
- app.install()
-
- archinstall.log('If your selections included multiple servers with the same port, you may have to reconfigure them.', fg="yellow", level=logging.INFO)
diff --git a/profiles/sway.py b/profiles/sway.py
deleted file mode 100644
index 5fbd3365..00000000
--- a/profiles/sway.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# A desktop environment using "Sway"
-from typing import Any, TYPE_CHECKING
-
-import archinstall
-from archinstall import Menu
-from archinstall.lib.menu.menu import MenuSelectionType
-
-if TYPE_CHECKING:
- _: Any
-
-is_top_level_profile = False
-
-__packages__ = [
- "sway",
- "swaylock",
- "swayidle",
- "waybar",
- "dmenu",
- "light",
- "grim",
- "slurp",
- "pavucontrol",
- "foot",
-]
-
-
-def _check_driver() -> bool:
- packages = archinstall.storage.get("gfx_driver_packages", [])
-
- if packages and "nvidia" in packages:
- prompt = _('The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?')
- choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run()
-
- if choice.value == Menu.no():
- return False
-
- return True
-
-def _get_system_privelege_control_preference():
- # need to activate seat service and add to seat group
- title = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)'))
- title += str(_('\n\nChoose an option to give Sway access to your hardware'))
- choice = Menu(title, ["polkit", "seatd"]).run()
-
- if choice.type_ != MenuSelectionType.Selection:
- return False
-
- archinstall.storage['sway_sys_priv_ctrl'] = [choice.value]
- archinstall.arguments['sway_sys_priv_ctrl'] = [choice.value]
- return True
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
- if not _get_system_privelege_control_preference():
- return False
-
- driver = archinstall.select_driver()
-
- if driver:
- archinstall.storage["gfx_driver_packages"] = driver
- if not _check_driver():
- return _prep_function(args, kwargs)
- return True
-
- return False
-
-
-"""
-def _post_install(*args, **kwargs):
- if "seatd" in sway_sys_priv_ctrl:
- print(_('After booting, add user(s) to the `seat` user group and re-login to use Sway'))
- return True
-"""
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("sway", "/somewhere/sway.py")
-# or through conventional import sway
-if __name__ == "sway":
- if not _check_driver():
- raise archinstall.lib.exceptions.HardwareIncompatibilityError(_('Sway does not support the proprietary nvidia drivers.'))
-
- # Install the Sway packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
- if "seatd" in archinstall.storage['sway_sys_priv_ctrl']:
- archinstall.storage['installation_session'].add_additional_packages(['seatd'])
- archinstall.storage['installation_session'].enable_service('seatd')
- elif "polkit" in archinstall.storage['sway_sys_priv_ctrl']:
- archinstall.storage['installation_session'].add_additional_packages(['polkit'])
- else:
- raise archinstall.lib.exceptions.ProfileError(_('Sway requires either seatd or polkit to run'))
-
- # Install the graphics driver packages
- archinstall.storage['installation_session'].add_additional_packages(f"xorg-server xorg-xinit {' '.join(archinstall.storage.get('gfx_driver_packages', None))}")
diff --git a/profiles/xfce4.py b/profiles/xfce4.py
deleted file mode 100644
index fbc68c10..00000000
--- a/profiles/xfce4.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# A desktop environment using "Xfce4"
-
-import archinstall
-
-is_top_level_profile = False
-
-__packages__ = [
- "xfce4",
- "xfce4-goodies",
- "pavucontrol",
- "lightdm",
- "lightdm-gtk-greeter",
- "gvfs",
- "xarchiver"
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- # XFCE requires a functional xorg installation.
- profile = archinstall.Profile(None, 'xorg')
- with profile.load_instructions(namespace='xorg.py') as imported:
- if hasattr(imported, '_prep_function'):
- return imported._prep_function()
- else:
- print('Deprecated (??): xorg profile has no _prep_function() anymore')
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("xfce4", "/somewhere/xfce4.py")
-# or through conventional import xfce4
-if __name__ == 'xfce4':
- # Install dependency profiles
- archinstall.storage['installation_session'].install_profile('xorg')
-
- # Install the XFCE4 packages
- archinstall.storage['installation_session'].add_additional_packages(__packages__)
-
- archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager
diff --git a/profiles/xorg.py b/profiles/xorg.py
deleted file mode 100644
index de45acd3..00000000
--- a/profiles/xorg.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# A system with "xorg" installed
-
-import archinstall
-import logging
-from archinstall.lib.hardware import __packages__ as __hwd__packages__
-
-is_top_level_profile = True
-
-__description__ = str(_('Installs a minimal system as well as xorg and graphics drivers.'))
-
-__packages__ = [
- 'dkms',
- 'xorg-server',
- 'xorg-xinit',
- 'nvidia-dkms',
- *__hwd__packages__,
-]
-
-
-def _prep_function(*args, **kwargs):
- """
- Magic function called by the importing installer
- before continuing any further. It also avoids executing any
- other code in this stage. So it's a safe way to ask the user
- for more input before any other installer steps start.
- """
-
- driver = archinstall.select_driver()
-
- if driver:
- archinstall.storage["gfx_driver_packages"] = driver
- return True
-
- # TODO: Add language section and/or merge it with the locale selected
- # earlier in for instance guided.py installer.
-
- return False
-
-
-# Ensures that this code only gets executed if executed
-# through importlib.util.spec_from_file_location("xorg", "/somewhere/xorg.py")
-# or through conventional import xorg
-if __name__ == 'xorg':
- try:
- if "nvidia" in archinstall.storage.get("gfx_driver_packages", []):
- if "linux-zen" in archinstall.storage['installation_session'].base_packages or "linux-lts" in archinstall.storage['installation_session'].base_packages:
- for kernel in archinstall.storage['installation_session'].kernels:
- archinstall.storage['installation_session'].add_additional_packages(f"{kernel}-headers") # Fixes https://github.com/archlinux/archinstall/issues/585
- archinstall.storage['installation_session'].add_additional_packages("dkms") # I've had kernel regen fail if it wasn't installed before nvidia-dkms
- archinstall.storage['installation_session'].add_additional_packages("xorg-server", "xorg-xinit", "nvidia-dkms")
- else:
- archinstall.storage['installation_session'].add_additional_packages(f"xorg-server", "xorg-xinit", *archinstall.storage.get('gfx_driver_packages', []))
- elif 'amdgpu' in archinstall.storage.get("gfx_driver_packages", []):
- # The order of these two are important if amdgpu is installed #808
- if 'amdgpu' in archinstall.storage['installation_session'].MODULES:
- archinstall.storage['installation_session'].MODULES.remove('amdgpu')
- archinstall.storage['installation_session'].MODULES.append('amdgpu')
-
- if 'radeon' in archinstall.storage['installation_session'].MODULES:
- archinstall.storage['installation_session'].MODULES.remove('radeon')
- archinstall.storage['installation_session'].MODULES.append('radeon')
-
- archinstall.storage['installation_session'].add_additional_packages(f"xorg-server", "xorg-xinit", *archinstall.storage.get('gfx_driver_packages', []))
- else:
- archinstall.storage['installation_session'].add_additional_packages(f"xorg-server", "xorg-xinit", *archinstall.storage.get('gfx_driver_packages', []))
- except Exception as err:
- archinstall.log(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}", level=logging.WARNING, fg="yellow")
- archinstall.storage['installation_session'].add_additional_packages("xorg-server", "xorg-xinit") # Prep didn't run, so there's no driver to install
diff --git a/pyproject.toml b/pyproject.toml
index 6e0fcb99..a1208bfc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,23 +4,23 @@ build-backend = "setuptools.build_meta"
[project]
name = "archinstall"
-dynamic = ["version", "entry-points"]
+dynamic = ["version"]
description = "Arch Linux installer - guided, templates etc."
authors = [
{name = "Anton Hvornum", email = "anton@hvornum.se"},
]
license = {text = "GPL-3.0-only"}
readme = "README.md"
-requires-python = ">=3.10"
-
+requires-python = ">=3.11"
keywords = ["linux", "arch", "archinstall", "installer"]
-
classifiers = [
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
"Operating System :: POSIX :: Linux",
]
+dependencies = [
+ "simple-term-menu==1.6.4",
+ "pyparted @ https://github.com//dcantrell/pyparted/archive/v3.13.0.tar.gz#sha512=26819e28d73420937874f52fda03eb50ab1b136574ea9867a69d46ae4976d38c4f26a2697fa70597eed90dd78a5ea209bafcc3227a17a7a5d63cff6d107c2b11",
+]
[project.urls]
Home = "https://archlinux.org"
@@ -28,27 +28,41 @@ Documentation = "https://archinstall.readthedocs.io/"
Source = "https://github.com/archlinux/archinstall"
[project.optional-dependencies]
+log = ["systemd_python==235"]
+dev = [
+ "mypy==1.10.0",
+ "pre-commit==3.7.0",
+]
doc = ["sphinx"]
[project.scripts]
archinstall = "archinstall:run_as_a_module"
+[tool.setuptools.dynamic]
+version = {attr = "archinstall.__version__"}
+readme = {file = ["README.rst", "USAGE.rst"]}
+
[tool.setuptools]
-packages = ["archinstall", "profiles", "examples"]
+packages = ["archinstall"]
[tool.setuptools.package-data]
-archinstall = [
- "examples/*.py",
- "profiles/*.py",
- "profiles/applications/*.py"
+# We could specify locales/lancuages.json etc instead, but catchall works too.
+"archinstall" = [
+ "**/*.py",
+ "**/*.mo",
+ "**/*.po",
+ "**/*.pot",
+ "**/*.json",
]
-[tool.setuptools.dynamic]
-version = {attr = "archinstall.__version__"}
+# [tool.setuptools.packages.find]
+# where = ["archinstall"]
[tool.mypy]
-python_version = "3.10"
+python_version = "3.11"
+files = "archinstall/"
exclude = "tests"
+check_untyped_defs=true
[tool.bandit]
targets = ["archinstall"]
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 00000000..39a2b6e9
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:base"
+ ]
+}
diff --git a/schema.json b/schema.json
index 9269e8e8..97cb42e1 100644
--- a/schema.json
+++ b/schema.json
@@ -12,14 +12,19 @@
"testing"
]
},
- "audio": {
+ "audio_config": {
"description": "Audio server to be installed",
- "type": "string",
- "enum": [
- "pipewire",
- "pulseaudio",
- "none"
- ]
+ "type": "object",
+ "properties": {
+ "audio": {
+ "description": "Audio server to be installed",
+ "type": "string",
+ "enum": [
+ "pipewire",
+ "pulseaudio"
+ ]
+ }
+ },
},
"bootloader": {
"description": "Bootloader to be installed",
@@ -30,6 +35,10 @@
"efistub"
]
},
+ "uki": {
+ "description": "Set to true to use unified kernel images",
+ "type": "boolean"
+ },
"custom-commands": {
"description": "Custom commands to be run post install",
"type": "array",
@@ -37,17 +46,6 @@
"type": "string"
}
},
- "gfx_driver": {
- "description": "Graphics Drivers to install if a desktop profile is used, ignored otherwise.",
- "type": "string",
- "enum": [
- "VMware / VirtualBox (open-source)",
- "Nvidia",
- "Intel (open-source)",
- "AMD / ATI (open-source)",
- "All open-source (default)"
- ]
- },
"harddrives": {
"description": "Path of device to be used",
"type": "array",
@@ -80,21 +78,26 @@
"description": "By default, it will autodetect the best region. Enter a region or a dictionary of regions and mirrors to use specific ones",
"type": "object"
},
- "nic": {
+ "network_config": {
"description": "Choose between NetworkManager, manual configuration, use systemd-networkd from the ISO or no configuration",
"type": "object",
"properties": {
"type": "string",
- "iface": "string",
- "dhcp": "boolean",
- "ip": "string",
- "gateway": "string",
- "dns": {
- "description": "List of DNS servers",
+ "nics": [
"type": "array",
"items": {
- "type": "string"
- }
+ "iface": "string",
+ "dhcp": "boolean",
+ "ip": "string",
+ "gateway": "string",
+ "dns": {
+ "description": "List of DNS servers",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
}
}
},
@@ -110,29 +113,82 @@
}
},
"profile": {
- "description": "Profiles are present in profiles/, use the name of a profile to install it",
- "type": "string",
- "enum": [
- "awesome",
- "bspwm",
- "budgie",
- "cinnamon",
- "cutefish",
- "deepin",
- "desktop",
- "enlightenment",
- "gnome",
- "i3",
- "kde",
- "lxqt",
- "mate",
- "minimal",
- "server",
- "sway",
- "xfce4",
- "xorg",
- "qtile"
- ]
+ "path": {
+ "description": "Local path or Url that points to a python file containing profile definitions",
+ "type": "string"
+ },
+ "main": {
+ "description": "Main top level profile selection",
+ "type": "string",
+ "enum": [
+ "desktop",
+ "minimal",
+ "server",
+ "xorg",
+ "custom"
+ ]
+ },
+ "details": {
+ "description": "Specific profile to be installed based on the 'main' selection; these profiles are present in default_profiles/, use the file name of a profile without the extension to install it (case insensitive)",
+ "type": "string",
+ "enum": [
+ "awesome",
+ "bspwm",
+ "budgie",
+ "cinnamon",
+ "cutefish",
+ "deepin",
+ "desktop",
+ "enlightenment",
+ "gnome",
+ "i3-wm",
+ "plasma",
+ "lxqt",
+ "mate",
+ "sway",
+ "xfce4",
+ "qtile",
+ "cockpit",
+ "docker",
+ "httpd",
+ "lighttpd",
+ "mariadb",
+ "nginx",
+ "postgresql",
+ "sshd",
+ "tomcat"
+ ]
+ },
+ "custom": {
+ "description": "Specific profile definitions for custom setup profiles)",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "packages": "string",
+ "services": "string",
+ "enabled": "boolean"
+ }
+ }
+ },
+ "gfx_driver": {
+ "description": "Graphics Drivers to install if a desktop profile is used, ignored otherwise.",
+ "type": "string",
+ "enum": [
+ "VMware / VirtualBox (open-source)",
+ "Intel (open-source)",
+ "AMD / ATI (open-source)",
+ "All open-source (default)",
+ "Nvidia (open kernel module for newer GPUs, Turing+)",
+ "Nvidia (open-source nouveau driver)",
+ "Nvidia (proprietary)"
+ ]
+ },
+ "greeter_type": {
+ "description": "Greeter type to install if a desktop profile is used, ignored otherwise.",
+ "type": "string",
+ "enum": ["lightdm", "sddm", "gdm"]
+ }
},
"services": {
"description": "Services to enable post-installation",