diff --git a/debian/changelog b/debian/changelog index 8eef05779..8d7d0030d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,44 @@ +freedombox (25.5) unstable; urgency=medium + + [ Sunil Mohan Adapa ] + * sogo: Fix a typo in tags + * ui: tags: Show tags on all cards pages if present + * ui: tags: Minor refactoring in menu filtering and sorting + * ui: tags: Add tag search/filter for system page + * ui: tags: Redirect to apps or system page appropriately + * ui: Minor change with renaming a variable + * ui: system: When canceling search stay on current page + * upgrades: Split dist upgrade into a separate module + * upgrades: Drop special handling for searx + * upgrades: Refactor code to disable snapshots + * upgrades: Refactor code for disabling Quassel during dist-upgrade + * upgrades: Use systemd-run to create transient service + * upgrades: Refactor code to hold packages + * upgrades: Split the main dist upgrade code + * upgrades: Relax list of packages to hold during dist upgrade + * upgrades: Perform sources file update more reliably + * upgrades: Refactor code to retrieve the new codename + * upgrades: Use systemd service status instead of flag file + * upgrades: Simplify dist upgrades checks using exceptions + * upgrades: Log apt output to journal during dist upgrade + * upgrades: Log messages using python logging framework + * upgrades: Simplify some global names + * action_utils: Ensure that package are unheld if dist upgrade fails + * upgrades: Perform easier checks first during dist upgrade + * upgrades: tests: Add unit tests for dist upgrade methods + * upgrades: Minor refactor to pre-dist upgrade checks + * upgrades: Use bind mounts to edit sources file only upon completion + * setup: Fix issue with pending app update and force upgrade + + [ Максим Горпиніч ] + * Translated using Weblate (Ukrainian) + + [ James Valleroy ] + * locale: Update translation strings + * doc: Fetch latest manual + + -- James Valleroy Mon, 10 Mar 2025 20:52:29 -0400 + freedombox (25.4.1~bpo12+1) bookworm-backports; urgency=medium * Rebuild for bookworm-backports. diff --git a/doc/manual/en/DebianUpgradeNotes.raw.wiki b/doc/manual/en/DebianUpgradeNotes.raw.wiki new file mode 100644 index 000000000..ac9e813f0 --- /dev/null +++ b/doc/manual/en/DebianUpgradeNotes.raw.wiki @@ -0,0 +1,26 @@ +<> + +## BEGIN_INCLUDE + +!FreedomBox is built on the Debian operating system. Approximately every two years, there is a new stable Debian release. !FreedomBox provides the option of [[FreedomBox/Manual/Upgrades#Auto-Update_to_Next_Stable_Release|automatically handling the upgrade]] to the new version of Debian after it is released. + +The current stable release is Debian 12 "bookworm". Below are additional considerations to have in mind when upgrading to a new release of Debian. + +== Debian 13 "trixie" == + +=== TT-RSS no longer supported === + +The [[DebianPts:tt-rss]] package has been removed from the next Debian release. It is recommended to move to one of the following options as a replacement: + * [[FreedomBox/Manual/Miniflux|Miniflux]] + * [[FreedomBox/Manual/Nextcloud|Nextcloud News]] + +=== Searx app no longer supported === + +The [[DebianPts:searx]] package has been removed from the next Debian release. There is currently no replacement for Searx app in !FreedomBox. Searx app will no longer received security updates, so it is recommended to stop using the app, and to uninstall it. + +## END_INCLUDE + +<> + +---- +CategoryFreedomBox diff --git a/doc/manual/en/ReleaseNotes.raw.wiki b/doc/manual/en/ReleaseNotes.raw.wiki index 8be31d721..cba5e5465 100644 --- a/doc/manual/en/ReleaseNotes.raw.wiki +++ b/doc/manual/en/ReleaseNotes.raw.wiki @@ -8,6 +8,43 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f The following are the release notes for each !FreedomBox version. +== FreedomBox 25.5 (2025-03-10) == + +=== Highlights === + + * ui: tags: Add tag search/filter for system page + +=== Other Changes === + + * action_utils: Ensure that package are unheld if dist upgrade fails + * locale: Update translations for Ukrainian + * setup: Fix issue with pending app update and force upgrade + * sogo: Fix a typo in tags + * ui: Minor change with renaming a variable + * ui: system: When canceling search stay on current page + * ui: tags: Minor refactoring in menu filtering and sorting + * ui: tags: Redirect to apps or system page appropriately + * ui: tags: Show tags on all cards pages if present + * upgrades: Drop special handling for searx + * upgrades: Log apt output to journal during dist upgrade + * upgrades: Log messages using python logging framework + * upgrades: Minor refactor to pre-dist upgrade checks + * upgrades: Perform easier checks first during dist upgrade + * upgrades: Perform sources file update more reliably + * upgrades: Refactor code for disabling Quassel during dist-upgrade + * upgrades: Refactor code to disable snapshots + * upgrades: Refactor code to hold packages + * upgrades: Refactor code to retrieve the new codename + * upgrades: Relax list of packages to hold during dist upgrade + * upgrades: Simplify dist upgrades checks using exceptions + * upgrades: Simplify some global names + * upgrades: Split dist upgrade into a separate module + * upgrades: Split the main dist upgrade code + * upgrades: Use bind mounts to edit sources file only upon completion + * upgrades: Use systemd service status instead of flag file + * upgrades: Use systemd-run to create transient service + * upgrades: tests: Add unit tests for dist upgrade methods + == FreedomBox 25.4.1 (2025-03-02) == * debian: Move e2fsprogs to Recommends diff --git a/doc/manual/en/Upgrades.raw.wiki b/doc/manual/en/Upgrades.raw.wiki index 7df7cf1e2..ddac27c1c 100644 --- a/doc/manual/en/Upgrades.raw.wiki +++ b/doc/manual/en/Upgrades.raw.wiki @@ -78,16 +78,10 @@ Auto-update is recommended for most users. However if you want to do the update * Take backups of your apps' data before performing the update. * Create a system snapshot before you begin. * General [[DebianUpgrade|instructions]] for upgrading Debian distribution are available. - * Some packages are known to have prompts during upgrade, due to modified conffiles. It is recommended not to upgrade these packages manually, but rather to allow !FreedomBox to handle their upgrade automatically. This applies to the following packages: - * bind9 + * Some packages are known to have prompts during upgrade, due to modified conffiles. It is recommended not to upgrade these packages manually, but rather to allow !FreedomBox to handle their upgrade automatically. For upgrade to Debian 13 "trixie", this applies to the following packages: * firewalld - * janus - * minetest-server * minidlna - * mumble-server * radicale - * roundcube-core - * tt-rss ## END_INCLUDE diff --git a/doc/manual/en/freedombox-manual.raw.wiki b/doc/manual/en/freedombox-manual.raw.wiki index aed5ca859..b680f0068 100644 --- a/doc/manual/en/freedombox-manual.raw.wiki +++ b/doc/manual/en/freedombox-manual.raw.wiki @@ -103,6 +103,10 @@ <> <> += Debian Upgrade Notes = + +<> + = Release Notes = <> diff --git a/doc/manual/es/Manual.raw.wiki b/doc/manual/es/Manual.raw.wiki index aed5ca859..b680f0068 100644 --- a/doc/manual/es/Manual.raw.wiki +++ b/doc/manual/es/Manual.raw.wiki @@ -103,6 +103,10 @@ <> <> += Debian Upgrade Notes = + +<> + = Release Notes = <> diff --git a/doc/manual/es/ReleaseNotes.raw.wiki b/doc/manual/es/ReleaseNotes.raw.wiki index 8be31d721..cba5e5465 100644 --- a/doc/manual/es/ReleaseNotes.raw.wiki +++ b/doc/manual/es/ReleaseNotes.raw.wiki @@ -8,6 +8,43 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f The following are the release notes for each !FreedomBox version. +== FreedomBox 25.5 (2025-03-10) == + +=== Highlights === + + * ui: tags: Add tag search/filter for system page + +=== Other Changes === + + * action_utils: Ensure that package are unheld if dist upgrade fails + * locale: Update translations for Ukrainian + * setup: Fix issue with pending app update and force upgrade + * sogo: Fix a typo in tags + * ui: Minor change with renaming a variable + * ui: system: When canceling search stay on current page + * ui: tags: Minor refactoring in menu filtering and sorting + * ui: tags: Redirect to apps or system page appropriately + * ui: tags: Show tags on all cards pages if present + * upgrades: Drop special handling for searx + * upgrades: Log apt output to journal during dist upgrade + * upgrades: Log messages using python logging framework + * upgrades: Minor refactor to pre-dist upgrade checks + * upgrades: Perform easier checks first during dist upgrade + * upgrades: Perform sources file update more reliably + * upgrades: Refactor code for disabling Quassel during dist-upgrade + * upgrades: Refactor code to disable snapshots + * upgrades: Refactor code to hold packages + * upgrades: Refactor code to retrieve the new codename + * upgrades: Relax list of packages to hold during dist upgrade + * upgrades: Simplify dist upgrades checks using exceptions + * upgrades: Simplify some global names + * upgrades: Split dist upgrade into a separate module + * upgrades: Split the main dist upgrade code + * upgrades: Use bind mounts to edit sources file only upon completion + * upgrades: Use systemd service status instead of flag file + * upgrades: Use systemd-run to create transient service + * upgrades: tests: Add unit tests for dist upgrade methods + == FreedomBox 25.4.1 (2025-03-02) == * debian: Move e2fsprogs to Recommends diff --git a/plinth/__init__.py b/plinth/__init__.py index 3d42e7e42..2e0e966f5 100644 --- a/plinth/__init__.py +++ b/plinth/__init__.py @@ -3,4 +3,4 @@ Package init file. """ -__version__ = '25.4.1' +__version__ = '25.5' diff --git a/plinth/action_utils.py b/plinth/action_utils.py index a1f26a19c..d03dadef4 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -449,14 +449,14 @@ def is_disk_image(): return os.path.exists('/var/lib/freedombox/is-freedombox-disk-image') -def run_apt_command(arguments): +def run_apt_command(arguments, stdout=subprocess.DEVNULL): """Run apt-get with provided arguments.""" command = ['apt-get', '--assume-yes', '--quiet=2'] + arguments env = os.environ.copy() env['DEBIAN_FRONTEND'] = 'noninteractive' - process = subprocess.run(command, stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, env=env, check=False) + process = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=stdout, + env=env, check=False) return process.returncode @@ -475,19 +475,20 @@ def apt_hold(packages): """ held_packages = [] - for package in packages: - current_hold = subprocess.check_output( - ['apt-mark', 'showhold', package]) - if not current_hold: - process = subprocess.run(['apt-mark', 'hold', package], - check=False) - if process.returncode == 0: # success - held_packages.append(package) + try: + for package in packages: + current_hold = subprocess.check_output( + ['apt-mark', 'showhold', package]) + if not current_hold: + process = subprocess.run(['apt-mark', 'hold', package], + check=False) + if process.returncode == 0: # success + held_packages.append(package) - yield held_packages - - for package in held_packages: - subprocess.check_call(['apt-mark', 'unhold', package]) + yield held_packages + finally: + for package in held_packages: + subprocess.check_call(['apt-mark', 'unhold', package]) @contextmanager diff --git a/plinth/locale/ar/LC_MESSAGES/django.po b/plinth/locale/ar/LC_MESSAGES/django.po index 74ad73d25..750045620 100644 --- a/plinth/locale/ar/LC_MESSAGES/django.po +++ b/plinth/locale/ar/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2023-10-19 06:18+0000\n" "Last-Translator: Shaik \n" "Language-Team: Arabic %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9065,6 +9053,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9095,11 +9091,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/ar_SA/LC_MESSAGES/django.po b/plinth/locale/ar_SA/LC_MESSAGES/django.po index 43b9e63de..d6a83c53b 100644 --- a/plinth/locale/ar_SA/LC_MESSAGES/django.po +++ b/plinth/locale/ar_SA/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2020-06-10 15:41+0000\n" "Last-Translator: aiman an \n" "Language-Team: Arabic (Saudi Arabia) %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9092,6 +9080,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9122,11 +9118,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/be/LC_MESSAGES/django.po b/plinth/locale/be/LC_MESSAGES/django.po index 3265b513c..6848c0ed6 100644 --- a/plinth/locale/be/LC_MESSAGES/django.po +++ b/plinth/locale/be/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -2548,7 +2548,7 @@ msgstr "" #: modules/firewall/templates/firewall.html:43 #: modules/letsencrypt/templates/letsencrypt.html:71 #: modules/snapshot/forms.py:23 modules/snapshot/forms.py:29 -#: templates/cards.html:36 +#: templates/cards.html:38 msgid "Disabled" msgstr "" @@ -4191,7 +4191,7 @@ msgstr "" msgid "Mumble" msgstr "" -#: modules/mumble/__init__.py:157 +#: modules/mumble/__init__.py:158 msgid "Mumble server is configured" msgstr "" @@ -6121,7 +6121,7 @@ msgid "" "existing calendars and address books." msgstr "" -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "" @@ -7027,10 +7027,6 @@ msgstr "" msgid "Webmail" msgstr "" -#: modules/sogo/manifest.py:73 -msgid "Calender" -msgstr "" - #: modules/sogo/manifest.py:74 msgid "Address book" msgstr "" @@ -7773,27 +7769,27 @@ msgid "" "manually run it now." msgstr "" -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " "update will be retried after 24 hours, if enabled." msgstr "" -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "" @@ -8815,14 +8811,6 @@ msgstr "" msgid "Service %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9029,6 +9017,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9057,11 +9053,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/bg/LC_MESSAGES/django.po b/plinth/locale/bg/LC_MESSAGES/django.po index d792c0f8b..3a1a421bc 100644 --- a/plinth/locale/bg/LC_MESSAGES/django.po +++ b/plinth/locale/bg/LC_MESSAGES/django.po @@ -7,10 +7,10 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-25 21:04+0000\n" -"Last-Translator: 109247019824 <109247019824@users.noreply.hosted.weblate.org>" -"\n" +"Last-Translator: 109247019824 <109247019824@users.noreply.hosted.weblate." +"org>\n" "Language-Team: Bulgarian \n" "Language: bg\n" @@ -2670,7 +2670,7 @@ msgstr "Включено" #: modules/firewall/templates/firewall.html:43 #: modules/letsencrypt/templates/letsencrypt.html:71 #: modules/snapshot/forms.py:23 modules/snapshot/forms.py:29 -#: templates/cards.html:36 +#: templates/cards.html:38 msgid "Disabled" msgstr "Изключено" @@ -4391,7 +4391,7 @@ msgstr "" msgid "Mumble" msgstr "" -#: modules/mumble/__init__.py:157 +#: modules/mumble/__init__.py:158 msgid "Mumble server is configured" msgstr "Сървърът на Mumble е настроен" @@ -6430,7 +6430,7 @@ msgid "" "existing calendars and address books." msgstr "" -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "" @@ -7366,10 +7366,6 @@ msgstr "" msgid "Webmail" msgstr "Webmail" -#: modules/sogo/manifest.py:73 -msgid "Calender" -msgstr "Календар" - #: modules/sogo/manifest.py:74 msgid "Address book" msgstr "Адресна книга" @@ -8201,11 +8197,22 @@ msgstr "" "Автоматичното обновяване, по подразбиране, се извършва всеки ден. Извършете " "го за първи път ръчно сега." -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "Започнато е обновяване на дистрибуцията" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" +"Започнато е обновяване до следващото стабилно издание. То може да отнеме " +"дълго време." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "Обновяването на дистрибуцията не може да бъде стартирано" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -8216,18 +8223,7 @@ msgstr "" "включено нов опит за обновяване на дистрибуцията ще бъде направен отново " "след 24 часа." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "Започнато е обновяване на дистрибуцията" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" -"Започнато е обновяване до следващото стабилно издание. То може да отнеме " -"дълго време." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "Проверка за задържани от обновяване пакети" @@ -9312,14 +9308,6 @@ msgstr "" msgid "Service %(service_name)s is not running." msgstr "Услугата %(service_name)s не работи." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Търсене по етикети" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Изчистване на всички етикети" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9532,6 +9520,14 @@ msgstr "" msgid "Update" msgstr "Обновяване" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Търсене по етикети" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Изчистване на всички етикети" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Резервно копие" @@ -9562,11 +9558,11 @@ msgstr "" msgid "Here" msgstr "Тук" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Настройките не са променени" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "преди премахване на {app_id}" @@ -9575,6 +9571,9 @@ msgstr "преди премахване на {app_id}" msgid "Gujarati" msgstr "Гуджарати" +#~ msgid "Calender" +#~ msgstr "Календар" + #~ msgid "Set Domain Name" #~ msgstr "Име на домейн" diff --git a/plinth/locale/bn/LC_MESSAGES/django.po b/plinth/locale/bn/LC_MESSAGES/django.po index 86d7168cf..dc81c6b8c 100644 --- a/plinth/locale/bn/LC_MESSAGES/django.po +++ b/plinth/locale/bn/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2021-06-16 07:33+0000\n" "Last-Translator: Oymate \n" "Language-Team: Bengali %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9104,6 +9092,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9132,11 +9128,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/cs/LC_MESSAGES/django.po b/plinth/locale/cs/LC_MESSAGES/django.po index 4f02cc977..01ae607ff 100644 --- a/plinth/locale/cs/LC_MESSAGES/django.po +++ b/plinth/locale/cs/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-27 05:06+0000\n" "Last-Translator: Jiří Podhorecký \n" "Language-Team: Czech %(service_name)s is not running." msgstr "Služba %(service_name)s není spuštěná." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Hledání pomocí štítků" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Vymazat všechny štítky" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10232,6 +10220,14 @@ msgstr "" msgid "Update" msgstr "Aktualizovat" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Hledání pomocí štítků" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Vymazat všechny štítky" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Záloha" @@ -10262,11 +10258,11 @@ msgstr "" msgid "Here" msgstr "Zde" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Nastavení se nezměnila" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "před odinstalací {app_id}" @@ -10275,6 +10271,9 @@ msgstr "před odinstalací {app_id}" msgid "Gujarati" msgstr "gudžarátština" +#~ msgid "Calender" +#~ msgstr "Kalendář" + #~ msgid "No status available." #~ msgstr "Stav není k dispozici." diff --git a/plinth/locale/da/LC_MESSAGES/django.po b/plinth/locale/da/LC_MESSAGES/django.po index 34dc179dd..b1f5e1285 100644 --- a/plinth/locale/da/LC_MESSAGES/django.po +++ b/plinth/locale/da/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:19+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Danish %(service_name)s is not running." msgstr "Tjenestesøgningstjenesten er ikke aktiv" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10491,6 +10479,14 @@ msgstr "" msgid "Update" msgstr "Opdater" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10526,11 +10522,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Indstilling uændret" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/de/LC_MESSAGES/django.po b/plinth/locale/de/LC_MESSAGES/django.po index 35067805b..429d4da30 100644 --- a/plinth/locale/de/LC_MESSAGES/django.po +++ b/plinth/locale/de/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-27 05:06+0000\n" "Last-Translator: Dietmar \n" "Language-Team: German ) und ihren Benutzernamen. Wenn Sie auf den Suchen-Knopf " "drücken, werden die bestehenden Kalender und Adressbücher aufgelistet." -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "Kalender" @@ -8138,10 +8138,6 @@ msgstr "" msgid "Webmail" msgstr "Webmail" -#: modules/sogo/manifest.py:73 -msgid "Calender" -msgstr "Kalender" - #: modules/sogo/manifest.py:74 msgid "Address book" msgstr "Adressbuch" @@ -9026,11 +9022,22 @@ msgstr "" "Die automatische Software-Aktualisierung wird standardmäßig täglich " "ausgeführt. Führen Sie es jetzt zum ersten Mal manuell aus." -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "Distributions-Upgrade gestartet" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" +"Das Update auf die nächste stabile Version wurde gestartet. Dies kann eine " +"lange Zeit in Anspruch nehmen." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "Distributions-Update konnte nicht gestartet werden" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -9041,18 +9048,7 @@ msgstr "" "mindestens 5 GB frei sind. Das Distributions-Update wird nach 24 Stunden " "erneut versucht, falls aktiviert." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "Distributions-Upgrade gestartet" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" -"Das Update auf die nächste stabile Version wurde gestartet. Dies kann eine " -"lange Zeit in Anspruch nehmen." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "Auf Paket-Sperren überprüfen" @@ -10230,14 +10226,6 @@ msgstr "Installation" msgid "Service %(service_name)s is not running." msgstr "Dienst %(service_name)s läuft nicht." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Suche mit Schlagwörtern" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Alle Schlagwörter löschen" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10465,6 +10453,14 @@ msgstr "" msgid "Update" msgstr "Aktualisieren" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Suche mit Schlagwörtern" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Alle Schlagwörter löschen" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Sicherungskopie" @@ -10495,11 +10491,11 @@ msgstr "" msgid "Here" msgstr "Hier" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Einstellung unverändert" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "vor der Deinstallation von {app_id}" @@ -10508,6 +10504,9 @@ msgstr "vor der Deinstallation von {app_id}" msgid "Gujarati" msgstr "Gujarati" +#~ msgid "Calender" +#~ msgstr "Kalender" + #~ msgid "No status available." #~ msgstr "Kein Status verfügbar." diff --git a/plinth/locale/django.pot b/plinth/locale/django.pot index 37cdc56d4..e446d67d9 100644 --- a/plinth/locale/django.pot +++ b/plinth/locale/django.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2549,7 +2549,7 @@ msgstr "" #: modules/firewall/templates/firewall.html:43 #: modules/letsencrypt/templates/letsencrypt.html:71 #: modules/snapshot/forms.py:23 modules/snapshot/forms.py:29 -#: templates/cards.html:36 +#: templates/cards.html:38 msgid "Disabled" msgstr "" @@ -4192,7 +4192,7 @@ msgstr "" msgid "Mumble" msgstr "" -#: modules/mumble/__init__.py:157 +#: modules/mumble/__init__.py:158 msgid "Mumble server is configured" msgstr "" @@ -6122,7 +6122,7 @@ msgid "" "existing calendars and address books." msgstr "" -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "" @@ -7028,10 +7028,6 @@ msgstr "" msgid "Webmail" msgstr "" -#: modules/sogo/manifest.py:73 -msgid "Calender" -msgstr "" - #: modules/sogo/manifest.py:74 msgid "Address book" msgstr "" @@ -7774,27 +7770,27 @@ msgid "" "manually run it now." msgstr "" -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " "update will be retried after 24 hours, if enabled." msgstr "" -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "" @@ -8816,14 +8812,6 @@ msgstr "" msgid "Service %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9030,6 +9018,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9058,11 +9054,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/el/LC_MESSAGES/django.po b/plinth/locale/el/LC_MESSAGES/django.po index f0f02cd38..7bb6afd0a 100644 --- a/plinth/locale/el/LC_MESSAGES/django.po +++ b/plinth/locale/el/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:20+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Greek %(service_name)s is not running." msgstr "Η υπηρεσία %(service_name)s δεν εκτελείται." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Αναζήτηση στο διαδίκτυο" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Αναζήτηση στο διαδίκτυο" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10781,6 +10763,18 @@ msgstr "" msgid "Update" msgstr "Ενημερωμένη έκδοση" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "Αναζήτηση στο διαδίκτυο" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "Αναζήτηση στο διαδίκτυο" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10816,11 +10810,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Οι ρυθμίσεις δεν άλλαξαν" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" @@ -10829,6 +10823,11 @@ msgstr "" msgid "Gujarati" msgstr "Gujarati" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "GNOME Calendar" + #, fuzzy #~| msgid "No repositories available." #~ msgid "No status available." diff --git a/plinth/locale/es/LC_MESSAGES/django.po b/plinth/locale/es/LC_MESSAGES/django.po index b594ce16a..f36b387b1 100644 --- a/plinth/locale/es/LC_MESSAGES/django.po +++ b/plinth/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2024-11-01 17:00+0000\n" "Last-Translator: gallegonovato \n" "Language-Team: Spanish %(service_name)s is not running." msgstr "El servidor %(service_name)s no se está ejecutando." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Buscar en la web" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Buscar en la web" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10518,6 +10500,18 @@ msgstr "" msgid "Update" msgstr "Actualización" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "Buscar en la web" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "Buscar en la web" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Copia de seguridad" @@ -10548,11 +10542,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Configuración sin cambio" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "antes de desinstalar {app_id}" @@ -10561,6 +10555,11 @@ msgstr "antes de desinstalar {app_id}" msgid "Gujarati" msgstr "Gujarati" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "Calendario GNOME" + #~ msgid "No status available." #~ msgstr "Estado de actualización No disponible." diff --git a/plinth/locale/fa/LC_MESSAGES/django.po b/plinth/locale/fa/LC_MESSAGES/django.po index fa33badc7..32c768253 100644 --- a/plinth/locale/fa/LC_MESSAGES/django.po +++ b/plinth/locale/fa/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:19+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Persian %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9991,6 +9979,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Create Connection" @@ -10023,11 +10019,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/fake/LC_MESSAGES/django.po b/plinth/locale/fake/LC_MESSAGES/django.po index aba9a5012..2720c1917 100644 --- a/plinth/locale/fake/LC_MESSAGES/django.po +++ b/plinth/locale/fake/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Plinth 0.6\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2016-01-31 22:24+0530\n" "Last-Translator: Sunil Mohan Adapa \n" "Language-Team: Plinth Developers %(service_name)s is not running." msgstr "SERVICE DISCOVERY SERVER IS NOT RUNNING" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10543,6 +10531,14 @@ msgstr "" msgid "Update" msgstr "UPDATE URL" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "PageKite Account" @@ -10578,11 +10574,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "SETTING UNCHANGED" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/fr/LC_MESSAGES/django.po b/plinth/locale/fr/LC_MESSAGES/django.po index cf282bdba..2ba9c1e99 100644 --- a/plinth/locale/fr/LC_MESSAGES/django.po +++ b/plinth/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-01-05 16:52+0000\n" "Last-Translator: Coucouf \n" "Language-Team: French %(service_name)s is not running." msgstr "Le service %(service_name)s n’est pas actif." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Recherches avec des étiquettes" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Supprimer toutes les étiquettes" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10499,6 +10485,14 @@ msgstr "" msgid "Update" msgstr "Mettre à jour" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Recherches avec des étiquettes" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Supprimer toutes les étiquettes" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Sauvegarder" @@ -10529,11 +10523,11 @@ msgstr "" msgid "Here" msgstr "Ici" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Paramètre inchangé" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "avant la désinstallation de {app_id}" @@ -10542,6 +10536,11 @@ msgstr "avant la désinstallation de {app_id}" msgid "Gujarati" msgstr "Gujarati" +#, fuzzy +#~| msgid "Calendar" +#~ msgid "Calender" +#~ msgstr "Calendrier" + #~ msgid "No status available." #~ msgstr "Aucun statut disponible." diff --git a/plinth/locale/gl/LC_MESSAGES/django.po b/plinth/locale/gl/LC_MESSAGES/django.po index fd88bc513..8690a37c8 100644 --- a/plinth/locale/gl/LC_MESSAGES/django.po +++ b/plinth/locale/gl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-12-30 10:51+0000\n" "Last-Translator: gallegonovato \n" "Language-Team: Galician %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9106,6 +9094,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9136,11 +9132,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/gu/LC_MESSAGES/django.po b/plinth/locale/gu/LC_MESSAGES/django.po index d2e270b5f..b387ca656 100644 --- a/plinth/locale/gu/LC_MESSAGES/django.po +++ b/plinth/locale/gu/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2021-01-18 12:32+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Gujarati %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9569,6 +9557,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9599,11 +9595,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "સેટિંગ યથાવત" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/hi/LC_MESSAGES/django.po b/plinth/locale/hi/LC_MESSAGES/django.po index f4f618488..20e56af8f 100644 --- a/plinth/locale/hi/LC_MESSAGES/django.po +++ b/plinth/locale/hi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2023-10-19 06:30+0000\n" "Last-Translator: Shaik \n" "Language-Team: Hindi %(service_name)s is not running." msgstr "सर्विस %(service_name)s नहीं चल रहा है." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "वेब सरच किजिये" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "वेब सरच किजिये" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10568,6 +10550,18 @@ msgstr "" msgid "Update" msgstr "अपडेट" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "वेब सरच किजिये" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "वेब सरच किजिये" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10603,11 +10597,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "सेटिंग स्थिर है" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" @@ -10616,6 +10610,11 @@ msgstr "" msgid "Gujarati" msgstr "" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "गनोम कैलेंडर" + #, fuzzy #~| msgid "Tor relay port available" #~ msgid "No status available." diff --git a/plinth/locale/hu/LC_MESSAGES/django.po b/plinth/locale/hu/LC_MESSAGES/django.po index 63e182e56..1737afa9e 100644 --- a/plinth/locale/hu/LC_MESSAGES/django.po +++ b/plinth/locale/hu/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-10-24 18:39+0000\n" "Last-Translator: Sunil Mohan Adapa \n" "Language-Team: Hungarian %(service_name)s is not running." msgstr "A szolgáltatás nem fut (%(service_name)s)." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Keresés a weben" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Keresés a weben" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10683,6 +10665,18 @@ msgstr "" msgid "Update" msgstr "Frissítés" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "Keresés a weben" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "Keresés a weben" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10718,11 +10712,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "A beállítás változatlan" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" @@ -10731,6 +10725,11 @@ msgstr "" msgid "Gujarati" msgstr "Gudzsaráti" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "GNOME Calendar" + #~ msgid "No status available." #~ msgstr "Nincs elérhető állapot." diff --git a/plinth/locale/id/LC_MESSAGES/django.po b/plinth/locale/id/LC_MESSAGES/django.po index 7fbb48109..dc39e5ac2 100644 --- a/plinth/locale/id/LC_MESSAGES/django.po +++ b/plinth/locale/id/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Indonesian (FreedomBox)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:19+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Indonesian %(service_name)s is not running." msgstr "Layanan %(service_name)s tidak berjalan." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Jelajahi web" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Jelajahi web" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9914,6 +9896,18 @@ msgstr "" msgid "Update" msgstr "Memperbarui" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "Jelajahi web" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "Jelajahi web" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9949,11 +9943,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" @@ -9962,6 +9956,11 @@ msgstr "" msgid "Gujarati" msgstr "Bahasa Gujarat" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "Kalender GNOME" + #, fuzzy #~| msgid "No libraries available." #~ msgid "No status available." diff --git a/plinth/locale/it/LC_MESSAGES/django.po b/plinth/locale/it/LC_MESSAGES/django.po index 02d14ac92..d41fb156c 100644 --- a/plinth/locale/it/LC_MESSAGES/django.po +++ b/plinth/locale/it/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:19+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Italian %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9977,6 +9965,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10010,11 +10006,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Impostazioni invariate" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/ja/LC_MESSAGES/django.po b/plinth/locale/ja/LC_MESSAGES/django.po index 1fd8071e7..d16a7664d 100644 --- a/plinth/locale/ja/LC_MESSAGES/django.po +++ b/plinth/locale/ja/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2023-05-07 23:50+0000\n" "Last-Translator: Nobuhiro Iwamatsu \n" "Language-Team: Japanese %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9034,6 +9022,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9062,11 +9058,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/kn/LC_MESSAGES/django.po b/plinth/locale/kn/LC_MESSAGES/django.po index 84d3d6de4..0a7b14719 100644 --- a/plinth/locale/kn/LC_MESSAGES/django.po +++ b/plinth/locale/kn/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2020-07-16 16:41+0000\n" "Last-Translator: Yogesh \n" "Language-Team: Kannada %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9034,6 +9022,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9062,11 +9058,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/lt/LC_MESSAGES/django.po b/plinth/locale/lt/LC_MESSAGES/django.po index 024fbc0d2..ce4377af3 100644 --- a/plinth/locale/lt/LC_MESSAGES/django.po +++ b/plinth/locale/lt/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:19+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Lithuanian %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9058,6 +9046,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9086,11 +9082,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/lv/LC_MESSAGES/django.po b/plinth/locale/lv/LC_MESSAGES/django.po index 9a357d72c..1a809d1ad 100644 --- a/plinth/locale/lv/LC_MESSAGES/django.po +++ b/plinth/locale/lv/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:20+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Latvian %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9057,6 +9045,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9085,11 +9081,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/nb/LC_MESSAGES/django.po b/plinth/locale/nb/LC_MESSAGES/django.po index 8463f3ad2..628ae9157 100644 --- a/plinth/locale/nb/LC_MESSAGES/django.po +++ b/plinth/locale/nb/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2024-10-27 23:30+0000\n" "Last-Translator: Sunil Mohan Adapa \n" "Language-Team: Norwegian Bokmål %(service_name)s is not running." msgstr "Tjenesten %(service_name)s kjører ikke." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Søk på nettet" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Søk på nettet" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10595,6 +10577,18 @@ msgstr "" msgid "Update" msgstr "Oppdater" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "Søk på nettet" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "Søk på nettet" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10629,11 +10623,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Oppsett uendret" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "før avinstallering av {app_id}" @@ -10642,6 +10636,11 @@ msgstr "før avinstallering av {app_id}" msgid "Gujarati" msgstr "Gujarati" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "GNOME-kalender" + #, fuzzy #~| msgid "No libraries available." #~ msgid "No status available." diff --git a/plinth/locale/nl/LC_MESSAGES/django.po b/plinth/locale/nl/LC_MESSAGES/django.po index ac8e6ccf3..85ebcce46 100644 --- a/plinth/locale/nl/LC_MESSAGES/django.po +++ b/plinth/locale/nl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-07 12:01+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Dutch ) en je gebruikersnaam. Klikken op de zoekknop zal de " "bestaande kalenders en adresboeken weergeven." -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "Kalender" @@ -7989,10 +7989,6 @@ msgstr "Thunderbird + SOGo verbinder" msgid "Webmail" msgstr "Webmail" -#: modules/sogo/manifest.py:73 -msgid "Calender" -msgstr "Kalender" - #: modules/sogo/manifest.py:74 msgid "Address book" msgstr "Adresboek" @@ -8857,11 +8853,20 @@ msgid "" "manually run it now." msgstr "" -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "Distributie-update gestart" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "Update naar volgende stabiele release gestart. Dit kan lang duren." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "Kan distributie-update niet starten" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -8871,16 +8876,7 @@ msgstr "" "te starten. Zorg ervoor dat ten minste 5 GB ruimte vrij is. Als " "ingeschakeld, wordt de distributie-update na 24 uur opnieuw geprobeerd." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "Distributie-update gestart" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "Update naar volgende stabiele release gestart. Dit kan lang duren." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "Controleer op blokkerende pakketten" @@ -10037,14 +10033,6 @@ msgstr "Installatie" msgid "Service %(service_name)s is not running." msgstr "Service %(service_name)s is niet actief." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Zoeken op labels" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Verwijder alle labels" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10272,6 +10260,14 @@ msgstr "" msgid "Update" msgstr "Update" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Zoeken op labels" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Verwijder alle labels" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Back-up" @@ -10302,11 +10298,11 @@ msgstr "" msgid "Here" msgstr "Hier" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Instelling onveranderd" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "voor het verwijderen van {app_id}" @@ -10315,6 +10311,9 @@ msgstr "voor het verwijderen van {app_id}" msgid "Gujarati" msgstr "Gujarati" +#~ msgid "Calender" +#~ msgstr "Kalender" + #~ msgid "No status available." #~ msgstr "Geen status beschikbaar." diff --git a/plinth/locale/pl/LC_MESSAGES/django.po b/plinth/locale/pl/LC_MESSAGES/django.po index 052b8b3a7..7ce2bc0ff 100644 --- a/plinth/locale/pl/LC_MESSAGES/django.po +++ b/plinth/locale/pl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2024-07-13 12:09+0000\n" "Last-Translator: Monika \n" "Language-Team: Polish %(service_name)s is not running." msgstr "Usługa %(service_name)s nie jest uruchomiona." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9939,6 +9927,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9972,11 +9968,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Ustawienie bez zmian" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/pt/LC_MESSAGES/django.po b/plinth/locale/pt/LC_MESSAGES/django.po index f9b8a12ab..e32290887 100644 --- a/plinth/locale/pt/LC_MESSAGES/django.po +++ b/plinth/locale/pt/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2023-05-22 15:50+0000\n" "Last-Translator: Frederico Gomes \n" "Language-Team: Portuguese %(service_name)s is not running." msgstr "O serviço %(service_name)s não está em execução." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9674,6 +9662,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9706,11 +9702,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Definição inalterada" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/ru/LC_MESSAGES/django.po b/plinth/locale/ru/LC_MESSAGES/django.po index a01069a4d..565c6f63c 100644 --- a/plinth/locale/ru/LC_MESSAGES/django.po +++ b/plinth/locale/ru/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-01-14 01:32+0000\n" "Last-Translator: gfbdrgng \n" "Language-Team: Russian ." -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "Календарь" @@ -8066,12 +8066,6 @@ msgstr "" msgid "Webmail" msgstr "Email" -#: modules/sogo/manifest.py:73 -#, fuzzy -#| msgid "Calendar" -msgid "Calender" -msgstr "Календарь" - #: modules/sogo/manifest.py:74 #, fuzzy #| msgid "Address" @@ -8949,11 +8943,22 @@ msgstr "" "По умолчанию автоматическое обновление программного обеспечения выполняется " "ежедневно. В первый раз запустите его вручную." -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "Началось обновление дистрибутива" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" +"Начато обновление до следующего стабильного выпуска. Это может занять много " +"времени." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "Не удалось запустить обновление дистрибутива" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -8963,18 +8968,7 @@ msgstr "" "дистрибутива. Пожалуйста, убедитесь, что свободно не менее 5 ГБ. Обновление " "дистрибутива будет повторно запущено через 24 часа, если это включено." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "Началось обновление дистрибутива" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" -"Начато обновление до следующего стабильного выпуска. Это может занять много " -"времени." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "Проверьте, не задерживается ли посылка" @@ -10132,14 +10126,6 @@ msgstr "Установка" msgid "Service %(service_name)s is not running." msgstr "Служба %(service_name)s не запущена." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Поиск по тегам" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Очистить все теги" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10369,6 +10355,14 @@ msgstr "" msgid "Update" msgstr "Обновление" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Поиск по тегам" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Очистить все теги" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Резервное копирование" @@ -10399,11 +10393,11 @@ msgstr "" msgid "Here" msgstr "Вот здесь" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Настройки без изменений" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "перед удалением {app_id}" @@ -10412,6 +10406,11 @@ msgstr "перед удалением {app_id}" msgid "Gujarati" msgstr "Гуджарати" +#, fuzzy +#~| msgid "Calendar" +#~ msgid "Calender" +#~ msgstr "Календарь" + #~ msgid "No status available." #~ msgstr "Статус недоступен." diff --git a/plinth/locale/si/LC_MESSAGES/django.po b/plinth/locale/si/LC_MESSAGES/django.po index 0590069b4..a5ed724a2 100644 --- a/plinth/locale/si/LC_MESSAGES/django.po +++ b/plinth/locale/si/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2021-04-27 13:32+0000\n" "Last-Translator: HelaBasa \n" "Language-Team: Sinhala %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9032,6 +9020,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "" @@ -9060,11 +9056,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/sl/LC_MESSAGES/django.po b/plinth/locale/sl/LC_MESSAGES/django.po index 6978e1802..be76bf213 100644 --- a/plinth/locale/sl/LC_MESSAGES/django.po +++ b/plinth/locale/sl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:19+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Slovenian %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9521,6 +9509,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9554,11 +9550,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/sq/LC_MESSAGES/django.po b/plinth/locale/sq/LC_MESSAGES/django.po index 5128bc9fc..51b8eebe0 100644 --- a/plinth/locale/sq/LC_MESSAGES/django.po +++ b/plinth/locale/sq/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-25 09:20+0000\n" "Last-Translator: Besnik Bleta \n" "Language-Team: Albanian %(service_name)s is not running." msgstr "Shërbimi %(service_name)s s’po xhiron." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Kërkoni me etiketa" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Spastroji krejt etiketat" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10347,6 +10335,14 @@ msgstr "" msgid "Update" msgstr "Përditësoje" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Kërkoni me etiketa" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Spastroji krejt etiketat" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Kopjeruajtje" @@ -10377,11 +10373,11 @@ msgstr "" msgid "Here" msgstr "Këtu" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Rregullim i pandryshuar" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "para çinstalimit të {app_id}" diff --git a/plinth/locale/sr/LC_MESSAGES/django.po b/plinth/locale/sr/LC_MESSAGES/django.po index e50c738f7..fd52d9dab 100644 --- a/plinth/locale/sr/LC_MESSAGES/django.po +++ b/plinth/locale/sr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2022-09-14 17:20+0000\n" "Last-Translator: ikmaak \n" "Language-Team: Serbian %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9336,6 +9324,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9368,11 +9364,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/sv/LC_MESSAGES/django.po b/plinth/locale/sv/LC_MESSAGES/django.po index 91dcf6a2b..3b810d64a 100644 --- a/plinth/locale/sv/LC_MESSAGES/django.po +++ b/plinth/locale/sv/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2024-07-30 01:31+0000\n" "Last-Translator: bittin1ddc447d824349b2 \n" "Language-Team: Swedish ) och ditt användarnamn. Om du klickar på sökknappen visas en lista " "över befintliga kalendrar och adressböcker." -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 #, fuzzy #| msgid "GNOME Calendar" msgid "Calendar" @@ -8218,12 +8218,6 @@ msgstr "" msgid "Webmail" msgstr "FairEmail" -#: modules/sogo/manifest.py:73 -#, fuzzy -#| msgid "GNOME Calendar" -msgid "Calender" -msgstr "GNOME-kalender" - #: modules/sogo/manifest.py:74 #, fuzzy #| msgid "Address" @@ -9093,11 +9087,22 @@ msgid "" "manually run it now." msgstr "" -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "Distributionsuppdateringen har startats" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" +"Startade uppdateringen till nästa stabila utgåva. Det kan ta lång tid att " +"slutföra." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "Det gick inte att starta distributionsuppdatering" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -9108,18 +9113,7 @@ msgstr "" "Distributionsuppdateringen kommer att göras ett nytt behov efter 24 timmar, " "om det är aktiverat." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "Distributionsuppdateringen har startats" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" -"Startade uppdateringen till nästa stabila utgåva. Det kan ta lång tid att " -"slutföra." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "" @@ -10288,18 +10282,6 @@ msgstr "Installation" msgid "Service %(service_name)s is not running." msgstr "Tjänsten %(service_name)s körs inte." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Sök på webben" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Sök på webben" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10529,6 +10511,18 @@ msgstr "" msgid "Update" msgstr "Uppdatera" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "Sök på webben" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "Sök på webben" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Säkerhetskopia" @@ -10559,11 +10553,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Instänllningar oförändrade" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "innan du avinstallerar {app_id}" @@ -10572,6 +10566,11 @@ msgstr "innan du avinstallerar {app_id}" msgid "Gujarati" msgstr "Gujarati" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "GNOME-kalender" + #~ msgid "No status available." #~ msgstr "Ingen status tillgänglig." diff --git a/plinth/locale/ta/LC_MESSAGES/django.po b/plinth/locale/ta/LC_MESSAGES/django.po index 06cf374a8..fa8d46343 100644 --- a/plinth/locale/ta/LC_MESSAGES/django.po +++ b/plinth/locale/ta/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2024-12-27 01:03+0000\n" "Last-Translator: James Valleroy \n" "Language-Team: Tamil %(service_name)s is not running." msgstr "பணி %(service_name)s இயங்கவில்லை." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "குறிச்சொற்களுடன் தேடுங்கள்" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search with tags" -msgid "Clear all tags" -msgstr "குறிச்சொற்களுடன் தேடுங்கள்" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10242,6 +10226,16 @@ msgstr "" msgid "Update" msgstr "புதுப்பிப்பு" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "குறிச்சொற்களுடன் தேடுங்கள்" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search with tags" +msgid "Clear all tags" +msgstr "குறிச்சொற்களுடன் தேடுங்கள்" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "காப்புப்பிரதி" @@ -10272,11 +10266,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "மாறாமல் அமைத்தல்" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "{app_id} ஐ நிறுவல் நீக்குவதற்கு முன்" @@ -10285,6 +10279,11 @@ msgstr "{app_id} ஐ நிறுவல் நீக்குவதற்கு msgid "Gujarati" msgstr "குசராத்தி" +#, fuzzy +#~| msgid "Calendar" +#~ msgid "Calender" +#~ msgstr "நாட்காட்டி" + #~ msgid "No status available." #~ msgstr "நிலை எதுவும் கிடைக்கவில்லை." diff --git a/plinth/locale/te/LC_MESSAGES/django.po b/plinth/locale/te/LC_MESSAGES/django.po index 3dd983a0e..9007f93d8 100644 --- a/plinth/locale/te/LC_MESSAGES/django.po +++ b/plinth/locale/te/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-22 10:04+0000\n" "Last-Translator: sai \n" "Language-Team: Telugu ." -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 #, fuzzy #| msgid "GNOME Calendar" msgid "Calendar" @@ -8039,12 +8039,6 @@ msgstr "" msgid "Webmail" msgstr "ఫెయిర్ఇమెయిల్" -#: modules/sogo/manifest.py:73 -#, fuzzy -#| msgid "GNOME Calendar" -msgid "Calender" -msgstr "కేలండర్" - #: modules/sogo/manifest.py:74 #, fuzzy #| msgid "Address" @@ -8892,11 +8886,20 @@ msgid "" "manually run it now." msgstr "" -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "పంపిణీ నవీకరణ ప్రారంభమైంది" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "తదుపరి స్థిరమైన విడుదలకు నవీకరణ ప్రారంభించబడింది. ఇది పూర్తి కావడానికి చాలా సమయం పట్టవచ్చు." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "పంపిణీ నవీకరణను ప్రారంభించడం సాధ్యపడలేదు" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -8905,16 +8908,7 @@ msgstr "" "పంపిణీ నవీకరణను ప్రారంభించడానికి రూట్ విభజనలో తగినంత ఖాళీ స్థలం లేదు. దయచేసి కనీసం 5 GB ఉచితంగా " "ఉండేలా చూసుకోండి. ప్రారంభించబడితే, పంపిణీ నవీకరణ 24 గంటల తర్వాత మళ్లీ ప్రయత్నించబడుతుంది." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "పంపిణీ నవీకరణ ప్రారంభమైంది" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "తదుపరి స్థిరమైన విడుదలకు నవీకరణ ప్రారంభించబడింది. ఇది పూర్తి కావడానికి చాలా సమయం పట్టవచ్చు." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "" @@ -10077,18 +10071,6 @@ msgstr "నిక్షిప్తం" msgid "Service %(service_name)s is not running." msgstr "%(service_name)s సేవ నడవడం లేదు." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "అంతర్జాలమును శోధింపుము" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "అంతర్జాలమును శోధింపుము" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10315,6 +10297,18 @@ msgstr "" msgid "Update" msgstr "నవీకరణ" +#: templates/tags.html:24 +#, fuzzy +#| msgid "Search the web" +msgid "Search with tags" +msgstr "అంతర్జాలమును శోధింపుము" + +#: templates/tags.html:37 +#, fuzzy +#| msgid "Search the web" +msgid "Clear all tags" +msgstr "అంతర్జాలమును శోధింపుము" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -10347,11 +10341,11 @@ msgstr "మొత్తం యాప్ డేటా మరియు కాన msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "మారకుండా అమర్చుతోంది" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "{app_id} ని అన్ఇన్‌స్టాల్ చేయడానికి ముందు" @@ -10361,6 +10355,11 @@ msgstr "{app_id} ని అన్ఇన్‌స్టాల్ చేయడా msgid "Gujarati" msgstr "గుజరాతీ" +#, fuzzy +#~| msgid "GNOME Calendar" +#~ msgid "Calender" +#~ msgstr "కేలండర్" + #~ msgid "No status available." #~ msgstr "స్థితి అందుబాటులో లేదు." diff --git a/plinth/locale/tr/LC_MESSAGES/django.po b/plinth/locale/tr/LC_MESSAGES/django.po index c4faa8d5d..27994df2e 100644 --- a/plinth/locale/tr/LC_MESSAGES/django.po +++ b/plinth/locale/tr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-25 06:15+0000\n" "Last-Translator: Burak Yavuz \n" "Language-Team: Turkish %(service_name)s is not running." msgstr "%(service_name)s hizmeti çalışmıyor." -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "Etiketler ile ara" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "Tüm etiketleri temizle" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10286,6 +10274,14 @@ msgstr "" msgid "Update" msgstr "Güncelle" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Etiketler ile ara" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Tüm etiketleri temizle" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Yedekle" @@ -10316,11 +10312,11 @@ msgstr "" msgid "Here" msgstr "Burada" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Ayar değişmedi" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "{app_id} kaldırılmadan önce" @@ -10329,6 +10325,9 @@ msgstr "{app_id} kaldırılmadan önce" msgid "Gujarati" msgstr "Gujarati" +#~ msgid "Calender" +#~ msgstr "Takvim" + #~ msgid "No status available." #~ msgstr "Mevcut durum yok." diff --git a/plinth/locale/uk/LC_MESSAGES/django.po b/plinth/locale/uk/LC_MESSAGES/django.po index c286e9d59..803d12867 100644 --- a/plinth/locale/uk/LC_MESSAGES/django.po +++ b/plinth/locale/uk/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" -"PO-Revision-Date: 2024-10-21 23:16+0000\n" -"Last-Translator: Ihor Hordiichuk \n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" +"PO-Revision-Date: 2025-03-11 00:06+0000\n" +"Last-Translator: Максим Горпиніч \n" "Language-Team: Ukrainian \n" "Language: uk\n" @@ -18,7 +18,7 @@ msgstr "" "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<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 5.8-rc\n" +"X-Generator: Weblate 5.10.3-dev\n" #: config.py:103 #, python-brace-format @@ -138,22 +138,17 @@ msgstr "" "пізніше." #: middleware.py:147 -#, fuzzy, python-brace-format -#| msgid "Page not found - %(box_name)s" +#, python-brace-format msgid "Page not found: {url}" -msgstr "Сторінку не знайдено - %(box_name)s" +msgstr "Сторінка не знайдена: {url}" #: middleware.py:150 -#, fuzzy -#| msgid "Error running apt-get" msgid "Error running operation." -msgstr "Помилка запуску apt-get" +msgstr "Помилка виконання операції." #: middleware.py:152 -#, fuzzy -#| msgid "Error running apt-get" msgid "Error loading page." -msgstr "Помилка запуску apt-get" +msgstr "Помилка завантаження сторінки." #: modules/apache/__init__.py:32 msgid "Apache HTTP Server" @@ -204,15 +199,15 @@ msgstr "Домен локальної мережі" #: modules/avahi/manifest.py:14 msgid "Auto-discovery" -msgstr "" +msgstr "Автовиявлення" #: modules/avahi/manifest.py:14 modules/backups/manifest.py:17 msgid "Local" -msgstr "" +msgstr "Місцевий" #: modules/avahi/manifest.py:14 msgid "mDNS" -msgstr "" +msgstr "mDNS" #: modules/backups/__init__.py:24 msgid "Backups allows creating and managing backup archives." @@ -323,6 +318,8 @@ msgid "" "In 24 hour format. Services may become temporarily unavailable while running " "backup operation at this time of the day." msgstr "" +"У 24-годинному форматі. Служби можуть стати тимчасово недоступними під час " +"виконання операції резервного копіювання в цей час доби." #: modules/backups/forms.py:85 modules/backups/forms.py:107 msgid "Included apps" @@ -367,6 +364,9 @@ msgid "" "file previously downloaded from the result of a successful backup on a " "{box_name}. It must have a .tar.gz extension." msgstr "" +"Виберіть файл резервної копії для завантаження з локального комп’ютера. Це " +"має бути файл, попередньо завантажений у результаті успішного резервного " +"копіювання на {box_name}. Він повинен мати розширення .tar.gz." #: modules/backups/forms.py:154 msgid "Repository path format incorrect." @@ -482,28 +482,20 @@ msgid "Restore" msgstr "Відновити" #: modules/backups/manifest.py:15 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted" -msgstr "Шифрування" +msgstr "Зашифровано" #: modules/backups/manifest.py:16 -#, fuzzy -#| msgid "Schedule" msgid "Schedules" -msgstr "Запланувати" +msgstr "Розклади" #: modules/backups/manifest.py:18 -#, fuzzy -#| msgid "Remove" msgid "Remote" -msgstr "Вилучити" +msgstr "Дистанційний" #: modules/backups/manifest.py:19 -#, fuzzy -#| msgid "App updated" msgid "App data" -msgstr "Застосунок оновлено" +msgstr "Дані програми" #: modules/backups/manifest.py:20 #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 @@ -514,7 +506,7 @@ msgstr "Налаштування" #: modules/backups/manifest.py:21 msgid "Borg" -msgstr "" +msgstr "Борг" #: modules/backups/privileged.py:31 msgid "" @@ -546,22 +538,20 @@ msgstr "" "Шлях репозиторію непорожній і не є наявним репозиторієм резервних копій." #: modules/backups/privileged.py:74 -#, fuzzy -#| msgid "A share with this name already exists." msgid "An archive with given name already exists in the repository." -msgstr "Ділянка з такою назвою вже існує." +msgstr "Архів із вказаною назвою вже існує в репозиторії." #: modules/backups/privileged.py:81 msgid "Archive with given name was not found in the repository." -msgstr "" +msgstr "Архів із вказаною назвою не знайдено в репозиторії." #: modules/backups/privileged.py:87 msgid "Backup system is busy with another operation." -msgstr "" +msgstr "Система резервного копіювання зайнята іншою операцією." #: modules/backups/privileged.py:92 msgid "Not enough space left on the disk or remote location." -msgstr "" +msgstr "Недостатньо місця на диску або у віддаленому місці." #: modules/backups/repository.py:94 msgid "Existing repository is not encrypted." @@ -805,10 +795,8 @@ msgid "Upload and restore a backup" msgstr "Вивантажити і відновити резервну копію" #: modules/backups/views.py:216 -#, fuzzy -#| msgid "Logged out successfully." msgid "Upload successful." -msgstr "Вийшли успішно." +msgstr "Завантаження успішне." #: modules/backups/views.py:254 msgid "No backup file found." @@ -831,10 +819,8 @@ msgid "Create backup repository" msgstr "Створити репозиторій резервних копій" #: modules/backups/views.py:350 -#, fuzzy -#| msgid "Added new remote SSH repository." msgid "Added new repository." -msgstr "Додано новий віддалений SSH-репозиторій." +msgstr "Додано новий репозиторій." #: modules/backups/views.py:364 msgid "Create remote backup repository" @@ -984,14 +970,12 @@ msgstr "Будь-який коментар, що допоможе запамʼя #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Обмін файлами і фрагментами коду" +msgstr "Обмін файлами" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Пастебін" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1118,10 +1102,8 @@ msgstr "" "Список серверів DNS, що розділені пробілами, до яких направлятимуться запити" #: modules/bind/manifest.py:16 -#, fuzzy -#| msgid "Enable DNSSEC" msgid "DNS" -msgstr "Увімкнути DNSSEC" +msgstr "DNS" #: modules/bind/manifest.py:17 modules/mumble/manifest.py:67 #: modules/radicale/manifest.py:91 modules/shadowsocks/forms.py:24 @@ -1130,7 +1112,7 @@ msgstr "Сервер" #: modules/bind/manifest.py:18 msgid "Resolver" -msgstr "" +msgstr "Розв'язати" #: modules/bind/templates/bind.html:11 msgid "Serving Domains" @@ -1240,19 +1222,15 @@ msgstr "Бібліотека з такою назвою вже існує." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "Електронна книга" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "Бібліотека ел. книжок" +msgstr "Бібліотека" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "Бібліотека ел. книжок" +msgstr "Читалка електронних книг" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1360,16 +1338,12 @@ msgid "Cockpit" msgstr "Cockpit" #: modules/cockpit/manifest.py:23 -#, fuzzy -#| msgid "Administration" msgid "Advanced administration" -msgstr "Адміністрування" +msgstr "Розширене адміністрування" #: modules/cockpit/manifest.py:24 -#, fuzzy -#| msgid "Sharing" msgid "Web terminal" -msgstr "Обмін" +msgstr "Веб-термінал" #: modules/cockpit/manifest.py:25 modules/storage/__init__.py:47 #: modules/storage/__init__.py:319 modules/storage/__init__.py:350 @@ -1378,10 +1352,8 @@ msgid "Storage" msgstr "Сховище" #: modules/cockpit/manifest.py:26 -#, fuzzy -#| msgid "Networks" msgid "Networking" -msgstr "Мережі" +msgstr "Мережа" #: modules/cockpit/manifest.py:27 modules/names/templates/names.html:20 msgid "Services" @@ -1389,7 +1361,7 @@ msgstr "Сервіси" #: modules/cockpit/manifest.py:28 msgid "Logs" -msgstr "" +msgstr "Журнали" #: modules/cockpit/manifest.py:29 modules/performance/__init__.py:16 #: modules/performance/__init__.py:40 @@ -1397,16 +1369,12 @@ msgid "Performance" msgstr "Продуктивність" #: modules/config/__init__.py:18 -#, fuzzy -#| msgid "" -#| "Here you can set some general configuration options like hostname, domain " -#| "name, webserver home page etc." msgid "" "Here you can set some general configuration options like webserver home page " "etc." msgstr "" -"Тут Ви можете задати деякі загальні параметри налаштувань, як-от назва " -"компʼютера, назва домена, домашня сторінка вебсервера тощо." +"Тут ви можете встановити деякі загальні параметри конфігурації, наприклад " +"домашню сторінку веб-сервера тощо." #: modules/config/__init__.py:40 msgid "General Configuration" @@ -1490,13 +1458,11 @@ msgstr "Домашня сторінка" #: modules/config/manifest.py:8 msgid "Logging" -msgstr "" +msgstr "Лісозаготівля" #: modules/config/manifest.py:8 -#, fuzzy -#| msgid "Advanced" msgid "Advanced apps" -msgstr "Розширені" +msgstr "Розширені програми" #: modules/config/views.py:41 #, python-brace-format @@ -1553,15 +1519,15 @@ msgstr "Невірний список URI серверів STUN/TURN" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Відеоконференція" #: modules/coturn/manifest.py:7 msgid "STUN" -msgstr "" +msgstr "STUN" #: modules/coturn/manifest.py:7 msgid "TURN" -msgstr "" +msgstr "TURN" #: modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" @@ -1606,14 +1572,10 @@ msgid "-- no time zone set --" msgstr "-- часовий пояс не обрано --" #: modules/datetime/manifest.py:15 -#, fuzzy -#| msgid "Network Interface" msgid "Network time" -msgstr "Мережевий інтерфейс" +msgstr "Час мережі" #: modules/datetime/manifest.py:15 -#, fuzzy -#| msgid "Time Zone" msgid "Timezone" msgstr "Часовий пояс" @@ -1655,24 +1617,18 @@ msgid "Bittorrent client written in Python/PyGTK" msgstr "Клієнт Bittorrent, написаний на Python/PyGTK" #: modules/deluge/manifest.py:21 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "BitTorrent Web Client" msgid "BitTorrent" -msgstr "Вебклієнт BitTorrent" +msgstr "BitTorrent" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Запустити вебклієнт" +msgstr "Веб-клієнт" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -1688,7 +1644,7 @@ msgstr "Діагностика" #: modules/diagnostics/__init__.py:99 msgid "skipped" -msgstr "" +msgstr "пропущено" #: modules/diagnostics/__init__.py:100 msgid "passed" @@ -1775,17 +1731,15 @@ msgstr "Якщо знайдено помилки, пробувати усуну #: modules/diagnostics/manifest.py:10 msgid "Detect problems" -msgstr "" +msgstr "Виявлення проблем" #: modules/diagnostics/manifest.py:10 -#, fuzzy -#| msgid "Repairing app" msgid "Repair" -msgstr "Лагодження застосунку" +msgstr "Ремонт" #: modules/diagnostics/manifest.py:10 msgid "Daily" -msgstr "" +msgstr "Щодня" #: modules/diagnostics/templates/diagnostics.html:11 msgid "Diagnostics Run" @@ -1899,16 +1853,16 @@ msgid "" "This service uses an external service to lookup public IP address. This can " "be configured in the privacy app." msgstr "" +"Ця служба використовує зовнішню службу для пошуку публічної IP-адреси. Це " +"можна налаштувати в програмі конфіденційності." #: modules/dynamicdns/__init__.py:64 msgid "Dynamic DNS Client" msgstr "Клієнт динамічної DNS" #: modules/dynamicdns/__init__.py:77 -#, fuzzy -#| msgid "Dynamic Domain Name" msgid "Dynamic Domain" -msgstr "Динамічна доменна назва" +msgstr "Динамічний домен" #: modules/dynamicdns/forms.py:20 msgid "" @@ -2028,10 +1982,8 @@ msgid "This field is required." msgstr "Це поле обовʼязкове." #: modules/dynamicdns/forms.py:138 modules/names/forms.py:126 -#, fuzzy -#| msgid "Content package added." msgid "Domain already exists." -msgstr "Додано пакунок вмісту." +msgstr "Домен вже існує." #: modules/dynamicdns/manifest.py:17 #: modules/dynamicdns/templates/dynamicdns.html:25 @@ -2043,20 +1995,16 @@ msgstr "Домен" #: modules/dynamicdns/manifest.py:17 msgid "Free" -msgstr "" +msgstr "Безкоштовно" #: modules/dynamicdns/manifest.py:17 -#, fuzzy -#| msgid "Server public key:" msgid "Needs public IP" -msgstr "Публічний ключ сервера:" +msgstr "Потрібен публічний IP" #: modules/dynamicdns/templates/dynamicdns-domain-delete.html:13 #: modules/names/templates/names-domain-delete.html:13 -#, fuzzy -#| msgid "Tor configuration is being updated" msgid "App configurations will be updated." -msgstr "Налаштування Tor оновлюються" +msgstr "Конфігурації програми буде оновлено." #: modules/dynamicdns/templates/dynamicdns.html:10 #: modules/email/templates/email.html:20 modules/names/manifest.py:11 @@ -2066,10 +2014,8 @@ msgstr "Домени" #: modules/dynamicdns/templates/dynamicdns.html:14 #: modules/dynamicdns/templates/dynamicdns.html:16 -#, fuzzy -#| msgid "Add Domains" msgid "Add Domain" -msgstr "Додати домени" +msgstr "Додати домен" #: modules/dynamicdns/templates/dynamicdns.html:26 msgid "Last update" @@ -2089,13 +2035,11 @@ msgstr "Дії" #: modules/dynamicdns/templates/dynamicdns.html:72 #, python-format msgid "Edit domain %(domain)s" -msgstr "" +msgstr "Редагувати домен %(domain)s" #: modules/dynamicdns/templates/dynamicdns.html:47 -#, fuzzy -#| msgid "Not set" msgid "Not yet" -msgstr "Не встановлено" +msgstr "Ще ні" #: modules/dynamicdns/templates/dynamicdns.html:53 msgid "Success" @@ -2106,16 +2050,13 @@ msgid "Failed" msgstr "Невдало" #: modules/dynamicdns/templates/dynamicdns.html:79 -#, fuzzy, python-format -#| msgid "Delete connection %(name)s" +#, python-format msgid "Delete domain %(domain)s" -msgstr "Видалит зʼєднання %(name)s" +msgstr "Видалити домен %(domain)s" #: modules/dynamicdns/templates/dynamicdns.html:91 -#, fuzzy -#| msgid "Cannot test: No domains are configured." msgid "No domains configured." -msgstr "Тестування не можливе: Нема налаштованих доменів." +msgstr "Домени не налаштовано." #: modules/dynamicdns/views.py:26 modules/dynamicdns/views.py:28 msgid "Connection timed out" @@ -2134,27 +2075,21 @@ msgid "Already up-to-date" msgstr "Вже оновлено" #: modules/dynamicdns/views.py:70 -#, fuzzy -#| msgid "Dynamic Domain Name" msgid "Add Dynamic Domain" -msgstr "Динамічна доменна назва" +msgstr "Додайте динамічний домен" #: modules/dynamicdns/views.py:72 -#, fuzzy -#| msgid "Dynamic Domain Name" msgid "Edit Dynamic Domain" -msgstr "Динамічна доменна назва" +msgstr "Редагувати динамічний домен" #: modules/dynamicdns/views.py:138 modules/names/views.py:137 #, python-brace-format msgid "Delete Domain {domain}?" -msgstr "" +msgstr "Видалити домен {domain}?" #: modules/dynamicdns/views.py:145 modules/names/views.py:143 -#, fuzzy -#| msgid "Client deleted." msgid "Domain deleted." -msgstr "Клієнт видалено." +msgstr "Домен видалено." #: modules/ejabberd/__init__.py:29 msgid "" @@ -2291,25 +2226,21 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Шифрування" +msgstr "Зашифровані повідомлення" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Аудіо чат" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 -#, fuzzy -#| msgid "Video Room" msgid "Video chat" -msgstr "Відеокімната" +msgstr "Відео чат" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 modules/firewall/manifest.py:10 #: modules/firewall/templates/firewall.html:16 @@ -2405,11 +2336,11 @@ msgstr "Postfix/Dovecot" #: modules/email/__init__.py:80 msgid "More emails" -msgstr "" +msgstr "Більше електронних листів" #: modules/email/__init__.py:80 msgid "Same mailbox" -msgstr "" +msgstr "Та сама поштова скринька" #: modules/email/__init__.py:82 msgid "My Email Aliases" @@ -2457,28 +2388,24 @@ msgid "Thunderbird" msgstr "Thunderbird" #: modules/email/manifest.py:37 -#, fuzzy -#| msgid "Thunderbird" msgid "Thunderbird Mobile" -msgstr "Thunderbird" +msgstr "Thunderbird Mobile" #: modules/email/manifest.py:52 msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" msgstr "Сервер електронної пошти" #: modules/email/manifest.py:82 msgid "IMAP" -msgstr "" +msgstr "IMAP" #: modules/email/manifest.py:82 msgid "Spam control" -msgstr "" +msgstr "Контроль спаму" #: modules/email/templates/email-aliases.html:13 #: modules/email/templates/email.html:15 @@ -2499,22 +2426,16 @@ msgid "Add" msgstr "Додати" #: modules/email/templates/email-dns.html:9 -#, fuzzy -#| msgid "DNS Records" msgid "DNS Records for domain:" -msgstr "Записи DNS" +msgstr "Записи DNS для домену:" #: modules/email/templates/email-dns.html:12 -#, fuzzy -#| msgid "" -#| "The following DNS records must be added manually on your primary domain " -#| "for the mail server to work properly." msgid "" "The following DNS records must be added manually on this domain for the mail " "server to work properly for this domain." msgstr "" -"Для коректної роботи на вашому основному домені поштового сервера потрібно " -"вручну додати наступні записи DNS." +"Наступні записи DNS необхідно додати вручну до цього домену, щоб поштовий " +"сервер працював належним чином." #: modules/email/templates/email-dns.html:23 #: modules/email/templates/email-dns.html:77 @@ -2544,10 +2465,8 @@ msgid "Host/Target/Value" msgstr "Хост/ціль/значення" #: modules/email/templates/email-dns.html:50 -#, fuzzy -#| msgid "Server hostname or IP address" msgid "Reverse DNS Records for IP Addresses" -msgstr "Назва хоста сервера або IP-адреса" +msgstr "Зворотні записи DNS для IP-адрес" #: modules/email/templates/email-dns.html:53 #, python-format @@ -2561,18 +2480,27 @@ msgid "" "part. Only one of your domains can have Revese DNS lookup configured unless " "you have multiple public IP addresses." msgstr "" +"Якщо ваш %(box_name)s працює в інфраструктурі хмарної служби, вам слід " +"налаштувати " +"Зворотний пошук DNS. Це не є обов’язковим, але значно покращує доставку " +"електронної пошти. Зворотний DNS не налаштовано там, де ваш звичайний DNS. " +"Ви повинні шукати його в налаштуваннях вашого VPS/ISP. Деякі постачальники " +"попередньо налаштовують частину IP-адреси для вас, і вам потрібно лише " +"встановити частину домену. Лише в одному з ваших доменів можна налаштувати " +"пошук Revese DNS, якщо у вас немає кількох публічних IP-адрес." #: modules/email/templates/email-dns.html:66 msgid "" "An external service is used to lookup public IP address to show in the " "following section. This can be configured in the privacy app." msgstr "" +"Зовнішня служба використовується для пошуку публічної IP-адреси для " +"відображення в наступному розділі. Це можна налаштувати в програмі " +"конфіденційності." #: modules/email/templates/email-dns.html:76 -#, fuzzy -#| msgid "Hostname" msgid "Host" -msgstr "Назва компʼютера" +msgstr "Хост" #: modules/email/templates/email.html:10 msgid "Manage Spam" @@ -2583,11 +2511,13 @@ msgid "" "The following domains are configured. View details to see the list of DNS " "entries to be made for the domain." msgstr "" +"Налаштовано такі домени. Перегляньте деталі, щоб побачити список записів " +"DNS, які потрібно зробити для домену." #: modules/email/templates/email.html:35 #, python-format msgid "View domain: %(domain)s" -msgstr "" +msgstr "Переглянути домен: %(domain)s" #: modules/featherwiki/__init__.py:25 #, python-brace-format @@ -2596,6 +2526,10 @@ msgid "" "a single HTML file on your {box_name}. You can use it as a personal wiki, as " "a web notebook, or for project documentation." msgstr "" +"Feather Wiki — це інструмент для створення простих самостійних вікі, кожна з " +"яких зберігається в одному файлі HTML на вашому {box_name}. Ви можете " +"використовувати його як особисту вікі, як веб-блокнот або для проектної " +"документації." #: modules/featherwiki/__init__.py:29 msgid "" @@ -2603,6 +2537,9 @@ msgid "" "wiki per topic. Customize each wiki to your liking with extensions and other " "customization options." msgstr "" +"Кожна вікі є невеликим файлом. Створіть скільки завгодно вікі, наприклад " +"одну вікі на тему. Налаштуйте кожну вікі на свій смак за допомогою розширень " +"та інших параметрів налаштування." #: modules/featherwiki/__init__.py:33 #, python-brace-format @@ -2610,6 +2547,8 @@ msgid "" "Feather Wiki is downloaded from {box_name} website and not from Debian. " "Wikis need to be upgraded to newer version manually." msgstr "" +"Feather Wiki завантажується з веб-сайту {box_name}, а не з Debian. Вікі " +"необхідно оновити до новішої версії вручну." #: modules/featherwiki/__init__.py:37 modules/tiddlywiki/__init__.py:41 #, python-brace-format @@ -2619,6 +2558,10 @@ msgid "" "{box_name} belonging to the wiki group. Simultaneous editing is not " "supported." msgstr "" +"За замовчуванням Wiki-сайти не є загальнодоступними, але їх можна " +"завантажити для спільного використання чи публікації. Їх може редагувати будь-який користувач {box_name}, який належить до " +"вікі-групи. Одночасне редагування не підтримується." #: modules/featherwiki/__init__.py:56 modules/ikiwiki/__init__.py:79 #: modules/tiddlywiki/__init__.py:61 @@ -2627,37 +2570,39 @@ msgstr "Перегляд і редагування застосунків вік #: modules/featherwiki/__init__.py:59 modules/featherwiki/manifest.py:9 msgid "Feather Wiki" -msgstr "" +msgstr "Перо Вікі" #: modules/featherwiki/forms.py:13 modules/tiddlywiki/forms.py:13 msgid "Name of the wiki file, with file extension \".html\"" -msgstr "" +msgstr "Назва вікі-файлу з розширенням \".html\"" #: modules/featherwiki/forms.py:15 modules/tiddlywiki/forms.py:15 msgid "" "Wiki title and description can be set from within the wiki. This file name " "is independent of the wiki title." msgstr "" +"Заголовок і опис вікі можна встановити зсередини вікі. Ця назва файлу не " +"залежить від назви вікі." #: modules/featherwiki/forms.py:23 modules/tiddlywiki/forms.py:23 msgid "New name for the wiki file, with file extension \".html\"" -msgstr "" +msgstr "Нова назва для вікі-файлу з розширенням \".html\"" #: modules/featherwiki/forms.py:25 modules/tiddlywiki/forms.py:25 msgid "Renaming the file has no effect on the title of the wiki." -msgstr "" +msgstr "Перейменування файлу не впливає на назву вікі." #: modules/featherwiki/forms.py:32 msgid "A Feather Wiki file with .html file extension" -msgstr "" +msgstr "Файл Feather Wiki з розширенням .html" #: modules/featherwiki/forms.py:35 msgid "Feather Wiki files must be in HTML format" -msgstr "" +msgstr "Файли Feather Wiki мають бути у форматі HTML" #: modules/featherwiki/forms.py:37 msgid "Upload an existing Feather Wiki file from this computer." -msgstr "" +msgstr "Завантажте існуючий файл Feather Wiki з цього комп’ютера." #: modules/featherwiki/manifest.py:18 #: modules/help/templates/help_about.html:108 modules/ikiwiki/manifest.py:15 @@ -2668,33 +2613,27 @@ msgstr "Вікі" #: modules/featherwiki/manifest.py:18 modules/infinoted/manifest.py:46 #: modules/tiddlywiki/manifest.py:20 msgid "Note taking" -msgstr "" +msgstr "Конспектування" #: modules/featherwiki/manifest.py:18 modules/ikiwiki/manifest.py:15 #: modules/mediawiki/manifest.py:25 modules/tiddlywiki/manifest.py:21 #: modules/wordpress/manifest.py:26 -#, fuzzy -#| msgid "Website Security" msgid "Website" -msgstr "Безпека вебсайту" +msgstr "Веб-сайт" #: modules/featherwiki/manifest.py:18 modules/tiddlywiki/manifest.py:25 msgid "Quine" -msgstr "" +msgstr "Куайн" #: modules/featherwiki/manifest.py:18 modules/nextcloud/manifest.py:56 #: modules/tiddlywiki/manifest.py:26 -#, fuzzy -#| msgid "Debian:" msgid "Non-Debian" -msgstr "Debian:" +msgstr "Не Debian" #: modules/featherwiki/templates/featherwiki_configure.html:12 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:12 -#, fuzzy -#| msgid "Manage Libraries" msgid "Manage Wikis" -msgstr "Керувати бібліотеками" +msgstr "Керуйте Wiki" #: modules/featherwiki/templates/featherwiki_configure.html:16 #: modules/featherwiki/templates/featherwiki_configure.html:18 @@ -2702,26 +2641,20 @@ msgstr "Керувати бібліотеками" #: modules/tiddlywiki/templates/tiddlywiki_configure.html:16 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:18 #: modules/tiddlywiki/views.py:47 -#, fuzzy -#| msgid "Create Wiki or Blog" msgid "Create Wiki" -msgstr "Створити вікі або блоґ" +msgstr "Створити Wiki" #: modules/featherwiki/templates/featherwiki_configure.html:21 #: modules/featherwiki/templates/featherwiki_configure.html:23 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:21 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:23 -#, fuzzy -#| msgid "Upload File" msgid "Upload Wiki" -msgstr "Викласти файл" +msgstr "Завантажити Wiki" #: modules/featherwiki/templates/featherwiki_configure.html:30 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:30 -#, fuzzy -#| msgid "No libraries available." msgid "No wikis available." -msgstr "Нема доступних бібліотек." +msgstr "Немає доступних вікі." #: modules/featherwiki/templates/featherwiki_configure.html:36 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:36 @@ -2733,7 +2666,7 @@ msgstr "Перейти до вікі %(wiki)s" #: modules/tiddlywiki/templates/tiddlywiki_configure.html:43 #, python-format msgid "Rename wiki %(wiki)s" -msgstr "" +msgstr "Перейменувати вікі %(wiki)s" #: modules/featherwiki/templates/featherwiki_configure.html:50 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:50 @@ -2743,80 +2676,63 @@ msgstr "Видалити вікі %(wiki)s" #: modules/featherwiki/templates/featherwiki_delete.html:12 #: modules/tiddlywiki/templates/tiddlywiki_delete.html:12 -#, fuzzy, python-format -#| msgid "Delete Wiki or Blog %(name)s" +#, python-format msgid "Delete wiki %(name)s" -msgstr "Видалити вікі або блоґ %(name)s" +msgstr "Видалити вікі %(name)s" #: modules/featherwiki/templates/featherwiki_delete.html:18 msgid "" "Hint: You can download a copy of this wiki from within " "Feather Wiki before deleting it." msgstr "" +"Підказка: Ви можете завантажити копію цієї вікі з Feather " +"Wiki перед її видаленням." #: modules/featherwiki/templates/featherwiki_delete.html:25 #: modules/tiddlywiki/templates/tiddlywiki_delete.html:25 -#, fuzzy -#| msgid "Delete this archive permanently?" msgid "Delete this wiki file permanently?" -msgstr "Остаточно видалити цей архів?" +msgstr "Видалити цей вікі-файл остаточно?" #: modules/featherwiki/templates/featherwiki_upload_file.html:20 #: modules/tiddlywiki/templates/tiddlywiki_upload_file.html:20 -#, fuzzy -#| msgid "Upload File" msgid "Upload" -msgstr "Викласти файл" +msgstr "Завантаження" #: modules/featherwiki/views.py:20 modules/tiddlywiki/views.py:20 -#, fuzzy -#| msgid "A share with this name already exists." msgid "A wiki file with the given name already exists." -msgstr "Ділянка з такою назвою вже існує." +msgstr "Вікі-файл із такою назвою вже існує." #: modules/featherwiki/views.py:54 modules/tiddlywiki/views.py:54 -#, fuzzy -#| msgid "Archive created." msgid "Wiki created." -msgstr "Архів створено." +msgstr "Wiki створено." #: modules/featherwiki/views.py:59 modules/tiddlywiki/views.py:59 -#, fuzzy -#| msgid "An error occurred while creating the library." msgid "An error occurred while creating the wiki." -msgstr "Помилка виникла під час створення бібліотеки." +msgstr "Під час створення вікі сталася помилка." #: modules/featherwiki/views.py:76 modules/tiddlywiki/views.py:76 -#, fuzzy -#| msgid "MediaWiki" msgid "Rename Wiki" -msgstr "MediaWiki" +msgstr "Перейменувати Wiki" #: modules/featherwiki/views.py:84 modules/tiddlywiki/views.py:84 msgid "Wiki renamed." -msgstr "" +msgstr "Wiki перейменовано." #: modules/featherwiki/views.py:89 modules/tiddlywiki/views.py:89 -#, fuzzy -#| msgid "An error occurred while creating the library." msgid "An error occurred while renaming the wiki." -msgstr "Помилка виникла під час створення бібліотеки." +msgstr "Під час перейменування вікі сталася помилка." #: modules/featherwiki/views.py:106 modules/tiddlywiki/views.py:106 -#, fuzzy -#| msgid "Upload File" msgid "Upload Wiki File" -msgstr "Викласти файл" +msgstr "Завантажте Wiki-файл" #: modules/featherwiki/views.py:115 modules/tiddlywiki/views.py:116 msgid "Wiki file added." -msgstr "" +msgstr "Wiki-файл додано." #: modules/featherwiki/views.py:119 modules/tiddlywiki/views.py:120 -#, fuzzy -#| msgid "Failed to add content package." msgid "Failed to add wiki file." -msgstr "Не вдалося додати пакунок вмісту." +msgstr "Не вдалося додати вікі-файл." #: modules/featherwiki/views.py:138 modules/tiddlywiki/views.py:139 #, python-brace-format @@ -2870,10 +2786,8 @@ msgid "Ports" msgstr "Порти" #: modules/firewall/manifest.py:10 -#, fuzzy -#| msgid "Blocked" msgid "Blocking" -msgstr "Блоковано" +msgstr "Блокування" #: modules/firewall/manifest.py:10 modules/networks/forms.py:319 #: modules/upgrades/manifest.py:10 @@ -2893,7 +2807,7 @@ msgstr "Дозволено" #: modules/firewall/templates/firewall.html:43 #: modules/letsencrypt/templates/letsencrypt.html:71 #: modules/snapshot/forms.py:23 modules/snapshot/forms.py:29 -#: templates/cards.html:36 +#: templates/cards.html:38 msgid "Disabled" msgstr "Вимкнено" @@ -2938,10 +2852,8 @@ msgstr "" "застосунком Cockpit." #: modules/first_boot/__init__.py:61 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup complete! Next steps:" -msgstr "Налаштування завершено!" +msgstr "Налаштування завершено! Наступні кроки:" #: modules/first_boot/__init__.py:63 #, python-brace-format @@ -2949,14 +2861,16 @@ msgid "" "Initial setup has been completed. Perform the next steps to make your " "{box_name} operational." msgstr "" +"Початкове налаштування завершено. Виконайте наступні кроки, щоб ваш " +"{box_name} почав працювати." #: modules/first_boot/__init__.py:66 msgid "Next steps" -msgstr "" +msgstr "Наступні кроки" #: modules/first_boot/__init__.py:73 msgid "See next steps" -msgstr "" +msgstr "Подивіться наступні дії" #: modules/first_boot/forms.py:14 #, python-brace-format @@ -2974,10 +2888,8 @@ msgid "Firstboot Wizard Secret" msgstr "Асистент під час першого завантаження" #: modules/first_boot/templates/firstboot_complete.html:14 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup Complete! Next Steps:" -msgstr "Налаштування завершено!" +msgstr "Налаштування завершено! Наступні кроки:" #: modules/first_boot/templates/firstboot_complete.html:21 #, python-format @@ -2985,6 +2897,9 @@ msgid "" "Automatic software update " "runs daily by default. For the first time, manually run it now." msgstr "" +"Автоматичне оновлення " +"програмного забезпечення виконується щодня за умовчанням. Уперше " +"запустіть його вручну." #: modules/first_boot/templates/firstboot_complete.html:30 #: modules/upgrades/templates/upgrades_configure.html:114 @@ -2996,6 +2911,8 @@ msgstr "Оновити зараз" msgid "" "Review privacy options." msgstr "" +"Перегляньте параметри " +"конфіденційності." #: modules/first_boot/templates/firstboot_complete.html:49 #, python-format @@ -3003,12 +2920,16 @@ msgid "" "Review and setup network " "connections. Change the default Wi-Fi password, if applicable." msgstr "" +"Перегляньте та налаштуйте мережеві підключення. Змініть стандартний пароль Wi-" +"Fi, якщо це можливо." #: modules/first_boot/templates/firstboot_complete.html:60 #, python-format msgid "" "Configure a domain name." msgstr "" +"Налаштуйте ім’я домену." #: modules/first_boot/templates/firstboot_complete.html:70 #, python-format @@ -3016,6 +2937,8 @@ msgid "" "Configure and schedule remote backups." msgstr "" +"Налаштуйте та заплануйте віддалене резервне копіювання." #: modules/first_boot/templates/firstboot_complete.html:81 #, python-format @@ -3023,6 +2946,8 @@ msgid "" "Put %(box_name)s to use by installing apps." msgstr "" +"Використовуйте %(box_name)s, установивши програми." #: modules/first_boot/templates/firstboot_welcome.html:29 msgid "Start Setup" @@ -3124,18 +3049,16 @@ msgid "Git" msgstr "Git" #: modules/gitweb/manifest.py:37 -#, fuzzy -#| msgid "Simple Git Hosting" msgid "Git hosting" -msgstr "Просте розміщення Git" +msgstr "Git хостинг" #: modules/gitweb/manifest.py:37 msgid "Version control" -msgstr "" +msgstr "Контроль версій" #: modules/gitweb/manifest.py:37 msgid "Developer tool" -msgstr "" +msgstr "Інструмент розробника" #: modules/gitweb/templates/gitweb_configure.html:13 msgid "Manage Repositories" @@ -3193,6 +3116,8 @@ msgstr "Змінити репозиторій" msgid "" "GNOME is a desktop environment that focuses on simplicity and ease of use." msgstr "" +"GNOME — це робоче середовище, яке зосереджується на простоті та легкості " +"використання." #: modules/gnome/__init__.py:21 #, python-brace-format @@ -3202,12 +3127,20 @@ msgid "" "suite, and other basic utilities are available. You may install further " "graphical applications using the software center provided within." msgstr "" +"Ця програма перетворює ваш {box_name} на настільний комп’ютер, якщо ви " +"фізично підключите до нього монітор, клавіатуру та мишу. Доступні браузер, " +"офісний пакет та інші базові утиліти. Ви можете інсталювати додаткові " +"графічні програми за допомогою центру програмного забезпечення, що входить " +"до нього." #: modules/gnome/__init__.py:26 msgid "" "This app is not suitable for low-end hardware. It requires at least 4GiB of " "RAM, 4GiB of disk space and a GPU capable of basic 3D acceleration." msgstr "" +"Ця програма не підходить для апаратного забезпечення низького класу. Для " +"цього потрібно принаймні 4 ГБ оперативної пам’яті, 4 ГБ дискового простору " +"та графічний процесор із базовим прискоренням 3D." #: modules/gnome/__init__.py:30 #, python-brace-format @@ -3216,40 +3149,37 @@ msgid "" "need to restart the machine for changes to take " "effect." msgstr "" +"Після встановлення, увімкнення, вимкнення або видалення програми вам " +"потрібно буде перезавантажити машину, щоб зміни " +"набули чинності." #: modules/gnome/__init__.py:48 -#, fuzzy -#| msgid "GNOME Files" msgid "GNOME" -msgstr "Файли GNOME" +msgstr "GNOME" #: modules/gnome/manifest.py:9 templates/clients.html:42 msgid "Desktop" msgstr "Стільниця" #: modules/gnome/manifest.py:10 -#, fuzzy -#| msgid "Tor Browser" msgid "Browser" -msgstr "Tor Browser" +msgstr "Браузер" #: modules/gnome/manifest.py:11 msgid "Office suite" -msgstr "" +msgstr "Офісний пакет" #: modules/gnome/manifest.py:12 -#, fuzzy -#| msgid "Software Update" msgid "Software store" -msgstr "Оновлення ПЗ" +msgstr "Магазин програмного забезпечення" #: modules/gnome/manifest.py:13 msgid "GUI" -msgstr "" +msgstr "GUI" #: modules/gnome/manifest.py:14 msgid "Graphical apps" -msgstr "" +msgstr "Графічні програми" #: modules/help/__init__.py:33 modules/help/templates/help_index.html:14 #: templates/help-menu.html:8 templates/help-menu.html:14 @@ -3287,10 +3217,8 @@ msgid "About" msgstr "Про FreedomBox" #: modules/help/templates/help_about.html:25 templates/messages.html:23 -#, fuzzy -#| msgid "Success" msgid "Success:" -msgstr "Успішно" +msgstr "Успіх:" #: modules/help/templates/help_about.html:29 #: modules/upgrades/templates/upgrades_configure.html:26 @@ -3376,10 +3304,8 @@ msgstr "" "Cockpit або SSH)." #: modules/help/templates/help_about.html:97 -#, fuzzy -#| msgid "Learn more" msgid "Learn" -msgstr "Дізнатися більше" +msgstr "Дізнайся" #: modules/help/templates/help_about.html:116 templates/toolbar.html:19 msgid "Donate" @@ -3387,17 +3313,15 @@ msgstr "Пожертви" #: modules/help/templates/help_about.html:119 msgid "Join project" -msgstr "" +msgstr "Приєднуйтесь до проекту" #: modules/help/templates/help_about.html:123 msgid "Translate" -msgstr "" +msgstr "Перекладайте" #: modules/help/templates/help_about.html:129 -#, fuzzy -#| msgid "Get Support" msgid "Support" -msgstr "Отримати підтримку" +msgstr "Підтримка" #: modules/help/templates/help_about.html:133 msgid "Forum" @@ -3631,16 +3555,12 @@ msgstr "" "про недолік." #: modules/help/templates/statuslog.html:27 -#, fuzzy -#| msgid "" -#| "Please remove any passwords or other personal information from the log " -#| "before submitting the bug report." msgid "" "Please remove any personal information from the log before submitting the " "bug report." msgstr "" -"Будь ласка, перед надсиланням звітів про помилки вилучіть із журналів будь-" -"яку особисту інформацію чи інформацію про паролі." +"Будь ласка, видаліть будь-яку особисту інформацію з журналу, перш ніж " +"надсилати звіт про помилку." #: modules/help/views.py:29 msgid "Documentation and FAQ" @@ -3694,7 +3614,7 @@ msgstr "Пароль обліківки адміна" #: modules/ikiwiki/manifest.py:15 modules/wordpress/manifest.py:26 msgid "Blog" -msgstr "" +msgstr "Блог" #: modules/ikiwiki/templates/ikiwiki_configure.html:12 msgid "Manage Wikis and Blogs" @@ -3807,7 +3727,7 @@ msgstr "" #: modules/infinoted/manifest.py:46 msgid "Collaborative editing" -msgstr "" +msgstr "Спільне редагування" #: modules/janus/__init__.py:23 msgid "Janus is a lightweight WebRTC server." @@ -3831,14 +3751,12 @@ msgid "Janus Video Room" msgstr "Відеокімната Janus" #: modules/janus/manifest.py:16 -#, fuzzy -#| msgid "WebRTC server" msgid "WebRTC" -msgstr "Сервер WebRTC" +msgstr "WebRTC" #: modules/janus/manifest.py:16 msgid "Web conference" -msgstr "" +msgstr "Веб-конференція" #: modules/janus/templates/janus_video_room.html:205 #: modules/jsxc/templates/jsxc_launch.html:117 templates/base.html:272 @@ -3858,16 +3776,12 @@ msgid "JSXC" msgstr "JSXC" #: modules/jsxc/manifest.py:16 -#, fuzzy -#| msgid "Web Search" msgid "Web chat" -msgstr "Вебпошук" +msgstr "Веб-чат" #: modules/jsxc/manifest.py:16 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Client" msgid "Client" -msgstr "Клієнт IRC" +msgstr "Клієнт" #: modules/kiwix/__init__.py:21 msgid "" @@ -3938,26 +3852,22 @@ msgstr "" "видалений для економії дискового простору." #: modules/kiwix/manifest.py:23 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Offline reader" -msgstr "Вікіпедія офлайн" +msgstr "Офлайн читалка" #: modules/kiwix/manifest.py:24 msgid "Archival" -msgstr "" +msgstr "Архівний" #: modules/kiwix/manifest.py:25 modules/shadowsocks/manifest.py:19 #: modules/shadowsocksserver/manifest.py:18 modules/tor/manifest.py:60 #: modules/torproxy/manifest.py:57 msgid "Censorship resistance" -msgstr "" +msgstr "Опір цензурі" #: modules/kiwix/manifest.py:26 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Wikipedia" -msgstr "Вікіпедія офлайн" +msgstr "Wikipedia" #: modules/kiwix/templates/kiwix-add-package.html:29 #, python-format @@ -4011,10 +3921,8 @@ msgid "Add a new content package" msgstr "Додати новий пакунок вмісту" #: modules/kiwix/views.py:76 -#, fuzzy -#| msgid "Content package added." msgid "Content package already exists." -msgstr "Додано пакунок вмісту." +msgstr "Пакет вмісту вже існує." #: modules/kiwix/views.py:79 msgid "Failed to add content package." @@ -4058,13 +3966,11 @@ msgstr "Сертифікати" #: modules/letsencrypt/manifest.py:11 msgid "HTTPS" -msgstr "" +msgstr "HTTPS" #: modules/letsencrypt/manifest.py:11 -#, fuzzy -#| msgid "Website Security" msgid "Web security" -msgstr "Безпека вебсайту" +msgstr "Веб-безпека" #: modules/letsencrypt/templates/letsencrypt.html:25 msgid "Certificate Status" @@ -4244,16 +4150,12 @@ msgid "FluffyChat" msgstr "FluffyChat" #: modules/matrixsynapse/manifest.py:101 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Chatroom" msgid "Chat room" -msgstr "Канал IRC" +msgstr "Кімната чату" #: modules/matrixsynapse/manifest.py:105 -#, fuzzy -#| msgid "Media streaming server" msgid "Matrix server" -msgstr "Сервер потокового медія" +msgstr "Матричний сервер" #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:18 msgid "" @@ -4267,24 +4169,13 @@ msgstr "" "вигляд @ім'я_користувача:назва_домену." #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:31 -#, fuzzy -#| msgid "" -#| "\n" -#| " Warning! Changing the domain name after this step " -#| "will\n" -#| " require uninstalling and reinstalling the app which will wipe app's " -#| "data.\n" -#| " " msgid "" "Warning! Changing the domain name after this step will " "require uninstalling and reinstalling the app which will wipe app's data." msgstr "" -"\n" -" Увага! Зміна назви домену після цього кроку потребує " -"видалення\n" -" та перевстановлення застосунку, що призведе до знищення даних " -"застосунку.\n" -" " +"Попередження! Зміна імені домену після цього кроку потребує " +"видалення та повторного встановлення програми, що призведе до видалення " +"даних програми." #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:42 #, python-format @@ -4584,20 +4475,16 @@ msgstr "" "типів." #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Updated server." msgid "Game server" -msgstr "Оновлено сервер." +msgstr "Ігровий сервер" #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Block Sandbox" msgid "Block sandbox" msgstr "Блокова пісочниця" #: modules/minetest/manifest.py:49 msgid "Platform" -msgstr "" +msgstr "Платформа" #: modules/minetest/templates/minetest.html:17 modules/networks/forms.py:105 #: modules/networks/forms.py:145 @@ -4655,24 +4542,20 @@ msgid "totem" msgstr "totem" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "Simple Media Server" msgid "Media server" -msgstr "Простий сервер медія" +msgstr "Медіасервер" #: modules/minidlna/manifest.py:116 msgid "Television" -msgstr "" +msgstr "Телебачення" #: modules/minidlna/manifest.py:116 msgid "UPnP" -msgstr "" +msgstr "UPnP" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "MiniDLNA" msgid "DLNA" -msgstr "MiniDLNA" +msgstr "DLNA" #: modules/minidlna/views.py:33 msgid "Updated media directory" @@ -4686,6 +4569,11 @@ msgid "" "subscribe to your favorite sites and access full article contents within the " "reader itself." msgstr "" +"Miniflux — це веб-інструмент, який об’єднує новини та оновлення блогів із " +"різних веб-сайтів в один централізований, зручний для читання формат. Він " +"має простий інтерфейс і зосереджений на читанні без відволікань. Ви можете " +"підписатися на ваші улюблені сайти та отримати доступ до повного вмісту " +"статей у самій програмі для читання." #: modules/miniflux/__init__.py:23 msgid "" @@ -4695,35 +4583,31 @@ msgid "" "are several third-party clients as well." msgstr "" +"Основні функції включають комбінації клавіш для швидкої навігації, " +"повнотекстового пошуку, фільтрації статей, категорій і вибраного. Miniflux " +"зберігає конфіденційність користувачів, видаляючи трекери. Основний " +"інтерфейс є веб-інтерфейсом. Також є декілька сторонніх клієнтів." #: modules/miniflux/__init__.py:42 modules/miniflux/manifest.py:10 msgid "Miniflux" -msgstr "" +msgstr "Мініфлюкс" #: modules/miniflux/forms.py:12 -#, fuzzy -#| msgid "Enter a valid username." msgid "Enter a username for the user." -msgstr "Уведіть коректне імʼя користувача." +msgstr "Введіть ім'я користувача для користувача." #: modules/miniflux/forms.py:16 msgid "Enter a strong password with a minimum of 6 characters." -msgstr "" +msgstr "Введіть надійний пароль щонайменше з 6 символів." #: modules/miniflux/forms.py:18 -#, fuzzy -#| msgid "Updating configuration" msgid "Password confirmation" -msgstr "Оновлення налаштувань" +msgstr "Підтвердження пароля" #: modules/miniflux/forms.py:20 -#, fuzzy -#| msgid "" -#| "Enter the password for user \"{user}\" to authorize account modifications." msgid "Enter the same password for confirmation." -msgstr "" -"Уведіть пароль для користувача \"{user}\", щоб авторизувати зміни облікового " -"запису." +msgstr "Введіть той самий пароль для підтвердження." #: modules/miniflux/forms.py:31 msgid "Passwords do not match." @@ -4731,91 +4615,80 @@ msgstr "Паролі не збігаються." #: modules/miniflux/manifest.py:18 msgid "Fluent Reader Lite" -msgstr "" +msgstr "Флейта зчитувач Lite" #: modules/miniflux/manifest.py:33 -#, fuzzy -#| msgid "News Feed Reader" msgid "Fluent Reader" -msgstr "Читання новинних стрічок" +msgstr "Вільний читач" #: modules/miniflux/manifest.py:46 msgid "FluxNews" -msgstr "" +msgstr "FluxNews" #: modules/miniflux/manifest.py:61 msgid "MiniFlutt" -msgstr "" +msgstr "MiniFlutt" #: modules/miniflux/manifest.py:71 msgid "NetNewsWire" -msgstr "" +msgstr "NetNewsWire" #: modules/miniflux/manifest.py:86 msgid "Newsflash" -msgstr "" +msgstr "Newsflash" #: modules/miniflux/manifest.py:96 -#, fuzzy -#| msgid "Read" msgid "Read You" -msgstr "Читати" +msgstr "Читати Вас" #: modules/miniflux/manifest.py:106 msgid "RSS Guard" -msgstr "" +msgstr "RSS Guard" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "News Feed Reader" msgid "Feed reader" -msgstr "Читання новинних стрічок" +msgstr "Читач каналів" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 msgid "News aggregation" -msgstr "" +msgstr "Агрегація новин" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "RSS" -msgstr "" +msgstr "RSS" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: modules/miniflux/templates/miniflux.html:14 msgid "" "Create an admin user to get started. Other users can be created from within " "Miniflux." msgstr "" +"Щоб почати, створіть користувача адміністратора. Інших користувачів можна " +"створити з Miniflux." #: modules/miniflux/templates/miniflux.html:22 #: modules/miniflux/templates/miniflux.html:24 -#, fuzzy -#| msgid "Create User" msgid "Create admin user" -msgstr "Створити користувача" +msgstr "Створити користувача адміністратора" #: modules/miniflux/templates/miniflux.html:27 #: modules/miniflux/templates/miniflux.html:29 -#, fuzzy -#| msgid "Set SuperUser Password" msgid "Reset user password" -msgstr "Задати пароль суперкористувача" +msgstr "Скинути пароль користувача" #: modules/miniflux/views.py:38 -#, fuzzy -#| msgid "Create User" msgid "Create Admin User" -msgstr "Створити користувача" +msgstr "Створити користувача адміністратора" #: modules/miniflux/views.py:48 -#, fuzzy, python-brace-format -#| msgid "Invalid username: {username}" +#, python-brace-format msgid "Created admin user: {username}" -msgstr "Некоректне імʼя користувача: {username}" +msgstr "Створено адміністратора: {username}" #: modules/miniflux/views.py:53 #, python-brace-format @@ -4823,16 +4696,13 @@ msgid "An error occurred while creating the user: {error}." msgstr "Помилка відбулася під час створення користувача: {error}." #: modules/miniflux/views.py:70 -#, fuzzy -#| msgid "Set SuperUser Password" msgid "Reset User Password" -msgstr "Задати пароль суперкористувача" +msgstr "Скинути пароль користувача" #: modules/miniflux/views.py:80 -#, fuzzy, python-brace-format -#| msgid "Invalid username: {username}" +#, python-brace-format msgid "Password reset for user: {username}" -msgstr "Некоректне імʼя користувача: {username}" +msgstr "Скидання пароля для користувача: {username}" #: modules/miniflux/views.py:85 #, python-brace-format @@ -4861,9 +4731,9 @@ msgstr "" msgid "Mumble" msgstr "Mumble" -#: modules/mumble/__init__.py:157 +#: modules/mumble/__init__.py:158 msgid "Mumble server is configured" -msgstr "" +msgstr "Сервер Mumble налаштовано" #: modules/mumble/forms.py:30 msgid "Set SuperUser Password" @@ -4912,7 +4782,7 @@ msgstr "Mumla" #: modules/mumble/manifest.py:67 msgid "Group conference" -msgstr "" +msgstr "Групова конференція" #: modules/mumble/views.py:43 msgid "SuperUser password successfully updated." @@ -4945,16 +4815,16 @@ msgstr "Назва сервісів" #: modules/names/__init__.py:68 msgid "Domain (regular)" -msgstr "" +msgstr "Домен (звичайний)" #: modules/names/__init__.py:175 msgid "Package systemd-resolved is installed" -msgstr "" +msgstr "Встановлено пакет systemd-resolved" #: modules/names/__init__.py:199 #, python-brace-format msgid "Resolve domain name: {domain}" -msgstr "" +msgstr "Розпізнати доменне ім’я: {domain}" #: modules/names/components.py:14 msgid "All" @@ -4971,10 +4841,12 @@ msgstr "Захищена оболонка" #: modules/names/forms.py:23 msgid "Use DNS-over-TLS for resolving domains (global preference)" msgstr "" +"Використовуйте DNS-over-TLS для визначення доменів (глобальні переваги)" #: modules/names/forms.py:51 msgid "Use DNSSEC when resolving domains (global preference)" msgstr "" +"Використовуйте DNSSEC під час визначення доменів (глобальне налаштування)" #: modules/names/forms.py:86 modules/names/manifest.py:12 msgid "Hostname" @@ -5016,10 +4888,8 @@ msgstr "" "назви не повинна перевищувати 253 знаки." #: modules/names/manifest.py:13 -#, fuzzy -#| msgid "Evolution" msgid "DNS Resolution" -msgstr "Evolution" +msgstr "Роздільна здатність DNS" #: modules/names/resolved.py:91 modules/names/resolved.py:101 #: modules/networks/forms.py:28 @@ -5033,28 +4903,24 @@ msgstr "так" #: modules/names/resolved.py:92 modules/networks/forms.py:29 #: modules/networks/views.py:124 msgid "opportunistic" -msgstr "" +msgstr "опортуністичний" #: modules/names/resolved.py:93 modules/names/resolved.py:103 #: modules/networks/forms.py:30 modules/networks/views.py:123 -#, fuzzy -#| msgid "Dino" msgid "no" -msgstr "Dino" +msgstr "ні" #: modules/names/resolved.py:102 msgid "allow-downgrade" -msgstr "" +msgstr "дозволити-понизити" #: modules/names/resolved.py:110 -#, fuzzy -#| msgid "Get Support" msgid "supported" -msgstr "Отримати підтримку" +msgstr "підтримується" #: modules/names/resolved.py:110 msgid "unsupported" -msgstr "" +msgstr "не підтримується" #: modules/names/templates/names.html:41 #: modules/networks/templates/connection_show.html:40 @@ -5070,50 +4936,44 @@ msgstr "Додати домени" #: modules/names/templates/names.html:87 msgid "Resolver Status" -msgstr "" +msgstr "Статус розв'язувача" #: modules/names/templates/names.html:97 msgid "Global" -msgstr "" +msgstr "Глобальний" #: modules/names/templates/names.html:99 msgid "Link" -msgstr "" +msgstr "Посилання" #: modules/names/templates/names.html:104 #: modules/networks/templates/connection_show.html:268 msgid "DNS-over-TLS" -msgstr "" +msgstr "DNS через TLS" #: modules/names/templates/names.html:108 -#, fuzzy -#| msgid "Enable DNSSEC" msgid "DNSSEC" -msgstr "Увімкнути DNSSEC" +msgstr "DNSSEC" #: modules/names/templates/names.html:113 -#, fuzzy -#| msgid "Second DNS Server" msgid "Current DNS Server" -msgstr "Другий сервер DNS" +msgstr "Поточний сервер DNS" #: modules/names/templates/names.html:119 -#, fuzzy -#| msgid "DNS Server" msgid "DNS Servers" -msgstr "Сервер DNS" +msgstr "DNS-сервери" #: modules/names/templates/names.html:129 -#, fuzzy -#| msgid "DNS Server" msgid "Fallback DNS Servers" -msgstr "Сервер DNS" +msgstr "Резервні DNS-сервери" #: modules/names/templates/names.html:143 msgid "" "systemd-resolved package is not installed. Install it for additional " "functionality." msgstr "" +"systemd-resolved пакет не встановлено. Встановіть його для додаткових " +"функцій." #: modules/names/templates/names.html:152 templates/setup.html:78 msgid "Install" @@ -5124,10 +4984,8 @@ msgid "Error retrieving status:" msgstr "Помилка отримання стану:" #: modules/names/views.py:83 -#, fuzzy -#| msgid "Hostname" msgid "Set Hostname" -msgstr "Назва компʼютера" +msgstr "Встановіть ім'я хоста" #: modules/names/views.py:101 #, python-brace-format @@ -5135,10 +4993,8 @@ msgid "Error setting hostname: {exception}" msgstr "Помилка параметру hostname: {exception}" #: modules/names/views.py:117 -#, fuzzy -#| msgid "Domain Name" msgid "Add Domain Name" -msgstr "Доменна назва" +msgstr "Додайте доменне ім’я" #: modules/networks/__init__.py:19 msgid "" @@ -5196,7 +5052,7 @@ msgstr "" #: modules/networks/forms.py:56 msgid "Use DNS-over-TLS" -msgstr "" +msgstr "Використовуйте DNS-over-TLS" #: modules/networks/forms.py:90 msgid "IPv4 Addressing Method" @@ -5310,7 +5166,7 @@ msgstr "Ігнорувати: Ігнорувати на цей спосіб ад #: modules/networks/forms.py:142 msgid "Disabled: Disable IPv6 for this connection" -msgstr "" +msgstr "Вимкнено: Вимкніть IPv6 для цього підключення" #: modules/networks/forms.py:147 msgid "Prefix" @@ -5600,14 +5456,12 @@ msgstr "Wi-Fi" #: modules/networks/manifest.py:8 modules/privoxy/manifest.py:10 #: modules/samba/manifest.py:90 -#, fuzzy -#| msgid "Local Network Domain" msgid "Local network" -msgstr "Домен локальної мережі" +msgstr "Локальна мережа" #: modules/networks/manifest.py:8 msgid "Topology" -msgstr "" +msgstr "Топологія" #: modules/networks/templates/connection_show.html:27 #, python-format @@ -5769,7 +5623,7 @@ msgstr "Зона фаєрволу" #: modules/networks/templates/connection_show.html:338 #: templates/internal-zone.html:13 templates/messages.html:20 msgid "Info:" -msgstr "" +msgstr "Інформація:" #: modules/networks/templates/connection_show.html:292 msgid "" @@ -5854,10 +5708,8 @@ msgstr "Змінити зʼєднання" #: modules/networks/templates/connections_fields.html:13 #: templates/messages.html:14 -#, fuzzy -#| msgid "Error" msgid "Error:" -msgstr "Помилка" +msgstr "Помилка:" #: modules/networks/templates/connections_fields.html:20 #: modules/users/templates/users_update.html:51 templates/messages.html:30 @@ -5865,8 +5717,6 @@ msgid "Close" msgstr "Закрити" #: modules/networks/templates/connections_fields.html:32 -#, fuzzy -#| msgid "Generic" msgid "General" msgstr "Загальний" @@ -6102,29 +5952,24 @@ msgstr "" #: modules/networks/templates/wifi_scan.html:14 msgid "No Wi-Fi device detected." -msgstr "" +msgstr "Пристрій Wi-Fi не виявлено." #: modules/networks/templates/wifi_scan.html:21 -#, fuzzy, python-format -#| msgid "Delete %(username)s" +#, python-format msgid "Device: %(interface_name)s" -msgstr "Видалити %(username)s" +msgstr "Пристрій: %(interface_name)s" #: modules/networks/templates/wifi_scan.html:27 -#, fuzzy -#| msgid "Last Connected Time" msgid "Last scanned: " -msgstr "Час останнього зʼєднання" +msgstr "Останнє сканування: " #: modules/networks/templates/wifi_scan.html:31 msgid "never" -msgstr "" +msgstr "ніколи" #: modules/networks/templates/wifi_scan.html:52 -#, fuzzy -#| msgid "Wi-Fi network not found" msgid "No Wi-Fi networks found." -msgstr "Не знайдено мережі Wi-Fi" +msgstr "Немає мереж Wi-Fi." #: modules/networks/views.py:27 msgid "disabled" @@ -6148,11 +5993,11 @@ msgstr "локальне посилання" #: modules/networks/views.py:32 msgid "dhcp" -msgstr "" +msgstr "dhcp" #: modules/networks/views.py:33 msgid "ignore" -msgstr "" +msgstr "ігнорувати" #: modules/networks/views.py:40 msgid "unmanaged" @@ -6304,10 +6149,8 @@ msgid "mesh point" msgstr "точка mesh" #: modules/networks/views.py:122 -#, fuzzy -#| msgid "Default" msgid "default" -msgstr "Типово" +msgstr "за замовчуванням" #: modules/networks/views.py:155 msgid "Cannot show connection: Connection not found." @@ -6460,17 +6303,15 @@ msgstr "" #: modules/nextcloud/manifest.py:56 modules/syncthing/manifest.py:58 msgid "File sync" -msgstr "" +msgstr "Синхронізація файлів" #: modules/nextcloud/manifest.py:56 modules/sharing/__init__.py:34 msgid "Sharing" msgstr "Обмін" #: modules/nextcloud/manifest.py:56 modules/sogo/manifest.py:72 -#, fuzzy -#| msgid "Group Share" msgid "Groupware" -msgstr "Груповий ресурс" +msgstr "Групова програма" #: modules/nextcloud/views.py:53 msgid "Password update failed. Please choose a stronger password." @@ -6513,15 +6354,13 @@ msgid "Tunnelblick" msgstr "Tunnelblick" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "DNS server" msgid "VPN server" -msgstr "Сервер DNS" +msgstr "VPN сервер" #: modules/openvpn/manifest.py:60 modules/pagekite/manifest.py:17 #: modules/wireguard/manifest.py:45 msgid "Remote access" -msgstr "" +msgstr "Віддалений доступ" #: modules/openvpn/templates/openvpn.html:12 msgid "Profile" @@ -6697,13 +6536,11 @@ msgstr "Цей сервіс вже існує" #: modules/pagekite/manifest.py:16 msgid "Reachability" -msgstr "" +msgstr "Досяжність" #: modules/pagekite/manifest.py:18 -#, fuzzy -#| msgid "Tunnelblick" msgid "Tunneling" -msgstr "Tunnelblick" +msgstr "Тунелювання" #: modules/pagekite/templates/pagekite_configure.html:13 msgid "Custom Services" @@ -6729,19 +6566,14 @@ msgid "Add custom PageKite service" msgstr "Додати власний сервіс PageKite" #: modules/pagekite/templates/pagekite_custom_services.html:19 -#, fuzzy -#| msgid "" -#| "Warning:
Your PageKite frontend server may not support all the " -#| "protocol/port combinations that you are able to define here. For example, " -#| "HTTPS on ports other than 443 is known to cause problems." msgid "" "Your PageKite frontend server may not support all the protocol/port " "combinations that you are able to define here. For example, HTTPS on ports " "other than 443 is known to cause problems." msgstr "" -"Попередження:
Ваш сервер інтерфейсу PageKite може не підтримувати " -"всі комбінації протоколів/портів, які ви можете визначити тут. Наприклад, " -"відомо, що HTTPS на портах, відмінних від 443, викликає проблеми." +"Ваш зовнішній сервер PageKite може не підтримувати всі комбінації протокол/" +"порт, які ви можете тут визначити. Наприклад, відомо, що HTTPS на портах, " +"відмінних від 443, викликає проблеми." #: modules/pagekite/utils.py:45 msgid "Web Server (HTTP)" @@ -6793,14 +6625,12 @@ msgstr "" "переглянуті за допомогою програми Cockpit." #: modules/performance/manifest.py:18 -#, fuzzy -#| msgid "System Monitoring" msgid "Monitoring" -msgstr "Моніторинг системи" +msgstr "Моніторинг" #: modules/performance/manifest.py:18 msgid "Resource utilization" -msgstr "" +msgstr "Використання ресурсів" #: modules/power/__init__.py:14 msgid "Restart or shut down the system." @@ -6812,13 +6642,11 @@ msgstr "Живлення" #: modules/power/manifest.py:10 msgid "Reboot" -msgstr "" +msgstr "Перезавантаження" #: modules/power/manifest.py:10 -#, fuzzy -#| msgid "Shut down" msgid "Shutdown" -msgstr "Вимкнути" +msgstr "Вимкнення" #: modules/power/templates/power.html:15 templates/base.html:181 #: templates/base.html:182 @@ -6885,6 +6713,12 @@ msgid "" "provided by the FreedomBox Foundation at https://ddns.freedombox.org/ip/. If " "empty, lookups are disabled and some functionality will fail." msgstr "" +"Додаткове значення. Ця URL-адреса використовується для визначення " +"загальнодоступної IP-адреси вашого {box_name}. URL-адреса має просто " +"повертати адресу IPv4 або IPv6, звідки надходить запит клієнта. За " +"умовчанням використовується служба FreedomBox Foundation за адресою https://" +"ddns.freedombox.org/ip/. Якщо поле пусте, пошук вимкнено, а деякі функції не " +"працюватимуть." #: modules/privacy/forms.py:25 msgid "Periodically submit a list of apps used (suggested)" @@ -6911,7 +6745,7 @@ msgstr "" #: modules/privacy/forms.py:37 msgid "Allow using fallback DNS servers" -msgstr "" +msgstr "Дозволити використання резервних DNS-серверів" #: modules/privacy/forms.py:39 msgid "" @@ -6920,28 +6754,26 @@ msgid "" "available. Can be disabled in most cases if network connectivity is stable " "and reliable." msgstr "" +"Використовуйте добре відомі загальнодоступні DNS-сервери для вирішення " +"доменних імен у незвичайних обставинах, коли DNS-сервери невідомі, але " +"доступне підключення до Інтернету. У більшості випадків його можна вимкнути, " +"якщо з’єднання з мережею стабільне та надійне." #: modules/privacy/forms.py:45 -#, fuzzy -#| msgid "URL to look up public IP" msgid "URL to look up public IP address" -msgstr "URL для пошуку публічної IP" +msgstr "URL-адреса для пошуку публічної IP-адреси" #: modules/privacy/manifest.py:10 msgid "Usage reporting" -msgstr "" +msgstr "Звіт про використання" #: modules/privacy/manifest.py:10 -#, fuzzy -#| msgid "Onion Service" msgid "External services" -msgstr "Сервіс Onion" +msgstr "Зовнішні служби" #: modules/privacy/manifest.py:10 -#, fuzzy -#| msgid "DNS Server" msgid "Fallback DNS" -msgstr "Сервер DNS" +msgstr "Резервний DNS" #: modules/privoxy/__init__.py:25 msgid "" @@ -6981,12 +6813,10 @@ msgstr "Доступ за адресою {url} через проксі {proxy} #: modules/privoxy/manifest.py:10 msgid "Ad blocker" -msgstr "" +msgstr "Блокувальник реклами" #: modules/privoxy/manifest.py:10 modules/shadowsocks/manifest.py:18 #: modules/torproxy/manifest.py:55 -#, fuzzy -#| msgid "Gobby Server" msgid "Proxy server" msgstr "Сервер Gobby" @@ -7028,27 +6858,21 @@ msgstr "Quasseldroid" #: modules/quassel/manifest.py:54 msgid "IRC" -msgstr "" +msgstr "IRC" #: modules/radicale/__init__.py:25 -#, fuzzy, python-brace-format -#| msgid "" -#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and " -#| "sharing of scheduling and contact data. To use Radicale, a supported client application is needed. Radicale can be " -#| "accessed by any user with a {box_name} login." +#, python-brace-format msgid "" "Radicale is a CalDAV and CardDAV server. It allows synchronization and " "sharing of scheduling and contact data. To use Radicale, a supported client application is needed. Radicale can be accessed by any user with a {box_name} login." msgstr "" -"Radicale – це сервер для CalDAV та CardDAV. Він дозволяє синхронізувати і " -"поширювати дані планування і контактів. Для використання Radicale необхідні " -"підтримувані клієнтські застосунки. До Radicale може мати " -"доступ будь-який користувач з імʼям входу для {box_name}." +"Radicale — це сервер CalDAV і CardDAV. Це дозволяє синхронізувати та " +"обмінюватися плануванням і контактними даними. Щоб використовувати Radicale, " +"потрібна підтримувана клієнтська програма. Radicale може отримати " +"доступ будь-який користувач із логіном {box_name}." #: modules/radicale/__init__.py:31 msgid "" @@ -7133,23 +6957,21 @@ msgstr "" "freedombox.address>) та ім'я користувача. Натиснувши на кнопку пошуку, ви " "побачите список наявних календарів та адресних книг." -#: modules/radicale/manifest.py:91 -#, fuzzy -#| msgid "GNOME Calendar" +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" -msgstr "Календар GNOME" +msgstr "Календар" #: modules/radicale/manifest.py:91 modules/roundcube/manifest.py:23 msgid "Contacts" -msgstr "" +msgstr "Контакти" #: modules/radicale/manifest.py:91 modules/sogo/manifest.py:75 msgid "CalDAV" -msgstr "" +msgstr "CalDAV" #: modules/radicale/manifest.py:91 modules/sogo/manifest.py:76 msgid "CardDAV" -msgstr "" +msgstr "CardDAV" #: modules/radicale/views.py:32 msgid "Access rights configuration updated" @@ -7218,10 +7040,8 @@ msgstr "" "під'єднатися." #: modules/roundcube/manifest.py:23 -#, fuzzy -#| msgid "FairEmail" msgid "Email" -msgstr "FairEmail" +msgstr "Електронна пошта" #: modules/rssbridge/__init__.py:21 msgid "" @@ -7242,19 +7062,16 @@ msgstr "" "reader." #: modules/rssbridge/__init__.py:28 -#, fuzzy, python-brace-format -#| msgid "" -#| "You can use RSS-Bridge with Tiny Tiny RSS to " -#| "follow various websites. When adding a feed, enable authentication and " -#| "use your {box_name} credentials." +#, python-brace-format msgid "" "You can use RSS-Bridge with Miniflux or Tiny Tiny RSS to follow various websites. When " "adding a feed, enable authentication and use your {box_name} credentials." msgstr "" -"RSS-Bridge можна використовувати разом із Tiny Tiny " -"RSS для відстеження різних вебсайтів. Під час додавання стрічки " -"дозвольте автентифікацію і використовуйте свої облікові дані {box_name}." +"Ви можете використовувати RSS-Bridge із Miniflux або Tiny Tiny RSS, щоб стежити за різними веб-" +"сайтами. Додаючи канал, увімкніть автентифікацію та використовуйте свої " +"облікові дані {box_name}." #: modules/rssbridge/__init__.py:48 modules/ttrss/__init__.py:47 msgid "Read and subscribe to news feeds" @@ -7274,14 +7091,12 @@ msgstr "" "Дозволити користуватися цим застосуноком кожному, хто має до нього доступ." #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "RSS Feed Generator" msgid "Feed generator" -msgstr "Ґенератор стрічок RSS" +msgstr "Генератор корму" #: modules/rssbridge/manifest.py:16 msgid "News" -msgstr "" +msgstr "Новини" #: modules/samba/__init__.py:23 msgid "" @@ -7358,22 +7173,16 @@ msgid "Dolphin" msgstr "Dolphin" #: modules/samba/manifest.py:91 -#, fuzzy -#| msgid "Network Interface" msgid "Network drive" -msgstr "Мережевий інтерфейс" +msgstr "Мережевий диск" #: modules/samba/manifest.py:92 -#, fuzzy -#| msgid "Media streaming server" msgid "Media storage" -msgstr "Сервер потокового медія" +msgstr "Зберігання медіа" #: modules/samba/manifest.py:93 -#, fuzzy -#| msgid "Backups" msgid "Backup storage" -msgstr "Резервні копії" +msgstr "Резервне сховище" #: modules/samba/templates/samba.html:20 msgid "Shares" @@ -7510,14 +7319,12 @@ msgid "Strict" msgstr "Строгий" #: modules/searx/manifest.py:17 -#, fuzzy -#| msgid "Web Search" msgid "Web search" -msgstr "Вебпошук" +msgstr "Пошук в Інтернеті" #: modules/searx/manifest.py:17 msgid "Metasearch Engine" -msgstr "" +msgstr "Метапошукова система" #: modules/security/forms.py:13 msgid "Fail2Ban (recommended)" @@ -7534,16 +7341,12 @@ msgstr "" "Інтернет-сервісів." #: modules/security/manifest.py:10 -#, fuzzy -#| msgid "Automatic" msgid "Automatic bans" -msgstr "Автоматично" +msgstr "Автоматичні заборони" #: modules/security/manifest.py:10 -#, fuzzy -#| msgid "Ports" msgid "Reports" -msgstr "Порти" +msgstr "Звіти" #: modules/security/templates/security.html:12 #: modules/security/templates/security.html:14 @@ -7687,13 +7490,11 @@ msgstr "Закладки" #: modules/shaarli/manifest.py:34 msgid "Link blog" -msgstr "" +msgstr "Посилання на блог" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "in use" msgid "Single user" -msgstr "використовується" +msgstr "Один користувач" #: modules/shadowsocks/__init__.py:18 modules/shadowsocksserver/__init__.py:18 msgid "" @@ -7760,22 +7561,16 @@ msgid "Encryption method. Must match setting on server." msgstr "Метод шифрування. Має відповідати налаштуванням на сервері." #: modules/shadowsocks/manifest.py:20 modules/shadowsocksserver/manifest.py:19 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted tunnel" -msgstr "Шифрування" +msgstr "Зашифрований тунель" #: modules/shadowsocks/manifest.py:21 -#, fuzzy -#| msgid "Endpoint" msgid "Entry point" -msgstr "Кінцева точка" +msgstr "Точка входу" #: modules/shadowsocks/manifest.py:22 modules/shadowsocksserver/manifest.py:21 -#, fuzzy -#| msgid "Shadowsocks Client" msgid "Shadowsocks" -msgstr "Клієнт Shadowsocks" +msgstr "Shadowsocks" #: modules/shadowsocksserver/__init__.py:26 #, python-brace-format @@ -7808,10 +7603,8 @@ msgstr "" "Метод шифрування. Клієнти повинні використовувати те саме налаштування." #: modules/shadowsocksserver/manifest.py:20 -#, fuzzy -#| msgid "Endpoint" msgid "Exit point" -msgstr "Кінцева точка" +msgstr "Точка виходу" #: modules/sharing/__init__.py:17 #, python-brace-format @@ -7869,10 +7662,8 @@ msgid "Shares should be either public or shared with at least one group" msgstr "Ділянка має бути спільною або публічною для щонайменше однієї групи" #: modules/sharing/manifest.py:19 modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Sharing" msgid "Web sharing" -msgstr "Обмін" +msgstr "Веб-обмін" #: modules/sharing/templates/sharing.html:18 #: modules/sharing/templates/sharing.html:21 @@ -8037,15 +7828,15 @@ msgstr "" #: modules/snapshot/manifest.py:14 msgid "Periodic" -msgstr "" +msgstr "Періодичні" #: modules/snapshot/manifest.py:14 msgid "Known good state" -msgstr "" +msgstr "Відомий хороший стан" #: modules/snapshot/manifest.py:14 msgid "Btrfs" -msgstr "" +msgstr "Btrfs" #: modules/snapshot/templates/snapshot_delete_selected.html:12 msgid "Delete the following snapshots permanently?" @@ -8173,6 +7964,10 @@ msgid "" "accessed with various mobile and desktop applications using the CalDAV and " "CardDAV standards." msgstr "" +"SOGo — це сервер групового програмного забезпечення, який надає багатий веб-" +"інтерфейс для електронної пошти, календаря, завдань і контактів. Доступ до " +"календаря, завдань і контактів також можна отримати за допомогою різних " +"мобільних і настільних програм за стандартами CalDAV і CardDAV." #: modules/sogo/__init__.py:26 #, python-brace-format @@ -8180,6 +7975,9 @@ msgid "" "Webmail works with the Postfix/Dovecot email " "server app to retrieve, manage, and send email." msgstr "" +"Веб-пошта працює з серверною програмою електронної пошти Postfix/Dovecot для отримання, керування й " +"надсилання електронної пошти." #: modules/sogo/__init__.py:30 #, python-brace-format @@ -8188,32 +7986,25 @@ msgid "" "their mailboxes by the email server app can be read and new mail can be sent " "out." msgstr "" +"Усі користувачі {box_name} можуть увійти та використовувати SOGo. Листи, " +"доставлені до їхніх поштових скриньок програмою сервера електронної пошти, " +"можна читати та надсилати нову пошту." #: modules/sogo/__init__.py:48 modules/sogo/manifest.py:11 msgid "SOGo" -msgstr "" +msgstr "SOGo" #: modules/sogo/manifest.py:19 msgid "Thunderbird + SOGo connector" -msgstr "" +msgstr "Thunderbird + роз'єм SOGo" #: modules/sogo/manifest.py:71 -#, fuzzy -#| msgid "FairEmail" msgid "Webmail" -msgstr "FairEmail" - -#: modules/sogo/manifest.py:73 -#, fuzzy -#| msgid "GNOME Calendar" -msgid "Calender" -msgstr "Календар GNOME" +msgstr "Веб-пошта" #: modules/sogo/manifest.py:74 -#, fuzzy -#| msgid "Address" msgid "Address book" -msgstr "Адреса" +msgstr "Адресна книга" #: modules/ssh/__init__.py:22 msgid "" @@ -8228,10 +8019,8 @@ msgstr "" "сервіси через певні зʼєднання." #: modules/ssh/__init__.py:42 -#, fuzzy -#| msgid "Secure Shell (SSH) Server" msgid "Secure Shell Server" -msgstr "Сервер захищеної оболонки (SSH)" +msgstr "Сервер Secure Shell" #: modules/ssh/__init__.py:75 msgid "Remotely login using Secure Shell (SSH)" @@ -8267,19 +8056,15 @@ msgstr "" #: modules/ssh/manifest.py:22 msgid "SSH" -msgstr "" +msgstr "SSH" #: modules/ssh/manifest.py:22 -#, fuzzy -#| msgid "Remove Location" msgid "Remote terminal" -msgstr "Вилучити розташування" +msgstr "Віддалений термінал" #: modules/ssh/manifest.py:22 -#, fuzzy -#| msgid "Fingerprint" msgid "Fingerprints" -msgstr "Відбиток" +msgstr "Відбитки пальців" #: modules/ssh/templates/ssh.html:11 msgid "Server Fingerprints" @@ -8455,7 +8240,7 @@ msgstr "Перейти до живлення" #: modules/storage/__init__.py:447 modules/storage/tests/test_storage.py:396 msgid "grub package is configured" -msgstr "" +msgstr "пакет grub налаштовано" #: modules/storage/forms.py:63 msgid "Invalid directory name." @@ -8495,23 +8280,19 @@ msgstr "Інший каталог (вкажіть нижче)" #: modules/storage/manifest.py:9 msgid "Disks" -msgstr "" +msgstr "Диски" #: modules/storage/manifest.py:9 msgid "Usage" -msgstr "" +msgstr "Використання" #: modules/storage/manifest.py:9 -#, fuzzy -#| msgid "Automatic" msgid "Auto-mount" -msgstr "Автоматично" +msgstr "Автомонтування" #: modules/storage/manifest.py:9 -#, fuzzy -#| msgid "Expand Root Partition" msgid "Expand partition" -msgstr "Розширити кореневий розділ" +msgstr "Розгорнути розділ" #: modules/storage/templates/storage.html:17 msgid "The following storage devices are in use:" @@ -8638,6 +8419,11 @@ msgid "" "several short notes called Tiddlers and link them together into a dense " "graph." msgstr "" +"TiddlyWiki — це інтерактивна програма, яка повністю працює у веб-браузері. " +"Кожна вікі — це окремий файл HTML, який зберігається на вашому {box_name}. " +"Замість того, щоб писати довгі вікі-сторінки, TiddlyWiki заохочує вас " +"написати кілька коротких нотаток під назвою Tiddlers і зв’язати їх у щільну " +"діаграму." #: modules/tiddlywiki/__init__.py:30 msgid "" @@ -8647,6 +8433,11 @@ msgid "" "TiddlyWiki. Encrypting individual tiddlers or password-protecting a wiki " "file is possible from within the application." msgstr "" +"Це універсальна програма з різноманітними варіантами використання – " +"нелінійний блокнот, веб-сайт, персональна база знань, система керування " +"завданнями та проектами, особистий щоденник тощо. Плагіни можуть розширити " +"функціональність TiddlyWiki. Шифрування окремих тиддлерів або захист вікі-" +"файлу паролем можливе з програми." #: modules/tiddlywiki/__init__.py:37 #, python-brace-format @@ -8654,44 +8445,48 @@ msgid "" "TiddlyWiki is downloaded from {box_name} website and not from Debian. Wikis " "need to be upgraded to newer version manually." msgstr "" +"TiddlyWiki завантажується з веб-сайту {box_name}, а не з Debian. Вікі " +"необхідно оновити до новішої версії вручну." #: modules/tiddlywiki/__init__.py:46 msgid "Create a new wiki or upload your existing wiki file to get started." -msgstr "" +msgstr "Створіть нову вікі або завантажте існуючий файл вікі, щоб почати." #: modules/tiddlywiki/__init__.py:64 modules/tiddlywiki/manifest.py:9 msgid "TiddlyWiki" -msgstr "" +msgstr "TiddlyWiki" #: modules/tiddlywiki/forms.py:32 msgid "A TiddlyWiki file with .html file extension" -msgstr "" +msgstr "Файл TiddlyWiki з розширенням .html" #: modules/tiddlywiki/forms.py:35 msgid "TiddlyWiki files must be in HTML format" -msgstr "" +msgstr "Файли TiddlyWiki мають бути у форматі HTML" #: modules/tiddlywiki/forms.py:37 msgid "Upload an existing TiddlyWiki file from this computer." -msgstr "" +msgstr "Завантажте наявний файл TiddlyWiki з цього комп’ютера." #: modules/tiddlywiki/manifest.py:22 msgid "Journal" -msgstr "" +msgstr "Журнал" #: modules/tiddlywiki/manifest.py:23 msgid "Digital garden" -msgstr "" +msgstr "Цифровий сад" #: modules/tiddlywiki/manifest.py:24 msgid "Zettelkasten" -msgstr "" +msgstr "Картотека" #: modules/tiddlywiki/templates/tiddlywiki_delete.html:18 msgid "" "Hint: You can download a copy of this wiki from within " "TiddlyWiki before deleting it." msgstr "" +"Підказка: Ви можете завантажити копію цієї вікі з " +"TiddlyWiki перед її видаленням." #: modules/tor/__init__.py:34 modules/torproxy/__init__.py:30 msgid "" @@ -8848,18 +8643,14 @@ msgid "Orbot: Proxy with Tor" msgstr "Orbot: проксі з Tor" #: modules/tor/manifest.py:57 -#, fuzzy -#| msgid "Onion Service" msgid "Onion services" -msgstr "Сервіс Onion" +msgstr "Цибулеві послуги" #: modules/tor/manifest.py:58 msgid "Relay" -msgstr "" +msgstr "Готові" #: modules/tor/manifest.py:59 modules/torproxy/manifest.py:56 -#, fuzzy -#| msgid "Anonymity Network" msgid "Anonymity network" msgstr "Мережа анонімності" @@ -9064,22 +8855,33 @@ msgid "FreedomBox Updated" msgstr "FreedomBox оновлено" #: modules/upgrades/__init__.py:138 -#, fuzzy -#| msgid "Software Update" msgid "Run software update manually" -msgstr "Оновлення ПЗ" +msgstr "Запустіть оновлення програмного забезпечення вручну" #: modules/upgrades/__init__.py:140 msgid "" "Automatic software update runs daily by default. For the first time, " "manually run it now." msgstr "" +"За замовчуванням автоматичне оновлення програмного забезпечення виконується " +"щодня. Уперше запустіть його вручну." -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "Оновлення дистрибутиву розпочато" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" +"Розпочато оновлення до наступного стабільного випуску. Це може зайняти " +"багато часу." + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "Не можливо запустити оновлення дистрибутиву" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " @@ -9090,20 +8892,9 @@ msgstr "" "місця. Оновлення дистрибутива буде повторено через 24 години, якщо його " "увімкнено." -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "Оновлення дистрибутиву розпочато" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" -"Розпочато оновлення до наступного стабільного випуску. Це може зайняти " -"багато часу." - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" -msgstr "" +msgstr "Перевірте наявність пакетів" #: modules/upgrades/forms.py:15 msgid "Enable auto-update" @@ -9132,13 +8923,11 @@ msgstr "Активувати оновлення частих функцій (р #: modules/upgrades/manifest.py:10 msgid "Reboots" -msgstr "" +msgstr "Перезавантаженні" #: modules/upgrades/manifest.py:10 -#, fuzzy -#| msgid "New value" msgid "New features" -msgstr "Нове значення" +msgstr "Нові функції" #: modules/upgrades/templates/backports-firstboot.html:26 msgid "" @@ -9394,8 +9183,6 @@ msgstr "" "кожен рядок. Порожні рядки і рядки, що починаються на # іґноруються." #: modules/users/forms.py:252 -#, fuzzy -#| msgid "Delete User" msgid "Delete user" msgstr "Видалити користувача" @@ -9404,12 +9191,13 @@ msgid "" "Deleting the user account will also remove all the files related to the " "user. Deleting files can be avoided by setting the user account as inactive." msgstr "" +"Видалення облікового запису користувача призведе до видалення всіх " +"пов’язаних із ним файлів. Видалення файлів можна уникнути, встановивши " +"обліковий запис користувача як неактивний." #: modules/users/forms.py:305 -#, fuzzy -#| msgid "Failed to add user to group." msgid "Failed to delete user." -msgstr "Не вдалося додати користувача до групи." +msgstr "Не вдалося видалити користувача." #: modules/users/forms.py:320 msgid "Renaming LDAP user failed." @@ -9445,16 +9233,12 @@ msgid "User account created, you are now logged in" msgstr "Обліковий запис користувача створено, Ви ввійшли в систему" #: modules/users/manifest.py:8 -#, fuzzy -#| msgid "Manage Snapshots" msgid "Manage accounts" -msgstr "Керування зрізами" +msgstr "Керувати обліковими записами" #: modules/users/manifest.py:8 -#, fuzzy -#| msgid "Permissions" msgid "App permissions" -msgstr "Дозволи" +msgstr "Дозволи програми" #: modules/users/templates/users_change_password.html:11 #, python-format @@ -9547,22 +9331,22 @@ msgid "Save Changes" msgstr "Зберегти зміни" #: modules/users/templates/users_update.html:46 -#, fuzzy, python-format -#| msgid "Delete user %(username)s permanently?" +#, python-format msgid "Delete user %(username)s and all the user's files?" -msgstr "Видалити користувача %(username)s назавжди?" +msgstr "Видалити користувача %(username)s і всі файли користувача?" #: modules/users/templates/users_update.html:56 msgid "" "Deleting a user account also removes all the files user's home directory. If " "you wish to keep these files, disable the user account instead." msgstr "" +"Видалення облікового запису користувача також видаляє всі файли з домашнього " +"каталогу користувача. Якщо ви хочете зберегти ці файли, замість цього " +"вимкніть обліковий запис користувача." #: modules/users/templates/users_update.html:65 -#, fuzzy -#| msgid "Delete files" msgid "Delete user and files" -msgstr "Видалити файли" +msgstr "Видалити користувача та файли" #: modules/users/templates/users_update.html:68 msgid "Cancel" @@ -9583,10 +9367,9 @@ msgid "Edit User" msgstr "Зміни користувача" #: modules/users/views.py:111 -#, fuzzy, python-format -#| msgid "User %(username)s created." +#, python-format msgid "User %(username)s deleted." -msgstr "Створено користувача %(username)s." +msgstr "Користувача %(username)s видалено." #: modules/users/views.py:130 msgid "Change Password" @@ -9713,10 +9496,8 @@ msgid "Typically checked for a VPN service through which all traffic is sent." msgstr "Зазвичай перевіряється служба VPN, через яку надсилається весь трафік." #: modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "IRC Client" msgid "VPN client" -msgstr "Клієнт IRC" +msgstr "VPN клієнт" #: modules/wireguard/templates/wireguard.html:10 msgid "As a Server" @@ -9997,7 +9778,7 @@ msgstr "" #: modules/wordpress/manifest.py:26 msgid "Content management system" -msgstr "" +msgstr "Система управління контентом" #: modules/zoph/__init__.py:24 #, python-brace-format @@ -10050,13 +9831,11 @@ msgstr "" #: modules/zoph/manifest.py:26 msgid "Photo" -msgstr "" +msgstr "Фото" #: modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Photo Organizer" msgid "Organizer" -msgstr "Упорядник світлин" +msgstr "Організатор" #: modules/zoph/templates/zoph-pre-setup.html:15 #: modules/zoph/templates/zoph-pre-setup.html:28 @@ -10077,10 +9856,9 @@ msgid "Generic" msgstr "Загальний" #: operation.py:120 -#, fuzzy, python-brace-format -#| msgid "Error: {name}: {exception_message}" +#, python-brace-format msgid "Error: {name}: {exception}" -msgstr "Помилка: {name}: {exception_message}" +msgstr "Помилка: {name}: {exception}" #: operation.py:123 #, python-brace-format @@ -10132,22 +9910,19 @@ msgid "Updating app" msgstr "Оновлення застосунку" #: setup.py:78 -#, fuzzy, python-brace-format -#| msgid "Error installing app: {error}" +#, python-brace-format msgid "Error installing app: {exception}" -msgstr "Помилка встановлення застосунку: {error}" +msgstr "Помилка встановлення програми: {exception}" #: setup.py:80 -#, fuzzy, python-brace-format -#| msgid "Error repairing app: {error}" +#, python-brace-format msgid "Error repairing app: {exception}" -msgstr "Помилка лагодження застосунку: {error}" +msgstr "Помилка відновлення програми: {exception}" #: setup.py:82 -#, fuzzy, python-brace-format -#| msgid "Error updating app: {error}" +#, python-brace-format msgid "Error updating app: {exception}" -msgstr "Помилка оновлення застосунку: {error}" +msgstr "Помилка оновлення програми: {exception}" #: setup.py:85 msgid "App installed." @@ -10264,18 +10039,6 @@ msgstr "Встановлення" msgid "Service %(service_name)s is not running." msgstr "Сервіс %(service_name)s не запущено." -#: templates/apps.html:33 -#, fuzzy -#| msgid "Search the web" -msgid "Search with tags" -msgstr "Пошук в Інтернеті" - -#: templates/apps.html:46 -#, fuzzy -#| msgid "Search the web" -msgid "Clear all tags" -msgstr "Пошук в Інтернеті" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -10503,6 +10266,14 @@ msgstr "" msgid "Update" msgstr "Оновити" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "Пошук за тегами" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "Очистити всі теги" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "Резервна копія" @@ -10531,13 +10302,13 @@ msgstr "" #: views.py:84 msgid "Here" -msgstr "" +msgstr "тут" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "Налаштування не змінено" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "перед видаленням {app_id}" @@ -10546,6 +10317,9 @@ msgstr "перед видаленням {app_id}" msgid "Gujarati" msgstr "Gujarati" +#~ msgid "Calender" +#~ msgstr "Календар" + #~ msgid "No status available." #~ msgstr "Стан недоступний." diff --git a/plinth/locale/vi/LC_MESSAGES/django.po b/plinth/locale/vi/LC_MESSAGES/django.po index b213565cc..0a985574d 100644 --- a/plinth/locale/vi/LC_MESSAGES/django.po +++ b/plinth/locale/vi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2021-07-28 08:34+0000\n" "Last-Translator: bruh \n" "Language-Team: Vietnamese %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9487,6 +9475,14 @@ msgstr "" msgid "Update" msgstr "Cập nhật" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9520,11 +9516,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/zh_Hans/LC_MESSAGES/django.po b/plinth/locale/zh_Hans/LC_MESSAGES/django.po index 0f2d7cf58..8ac8afc89 100644 --- a/plinth/locale/zh_Hans/LC_MESSAGES/django.po +++ b/plinth/locale/zh_Hans/LC_MESSAGES/django.po @@ -7,10 +7,10 @@ msgid "" msgstr "" "Project-Id-Version: Plinth\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-25 09:20+0000\n" -"Last-Translator: 大王叫我来巡山 " -"\n" +"Last-Translator: 大王叫我来巡山 \n" "Language-Team: Chinese (Simplified Han script) \n" "Language: zh_Hans\n" @@ -2651,7 +2651,7 @@ msgstr "启用" #: modules/firewall/templates/firewall.html:43 #: modules/letsencrypt/templates/letsencrypt.html:71 #: modules/snapshot/forms.py:23 modules/snapshot/forms.py:29 -#: templates/cards.html:36 +#: templates/cards.html:38 msgid "Disabled" msgstr "已禁用" @@ -4340,7 +4340,7 @@ msgstr "" msgid "Mumble" msgstr "" -#: modules/mumble/__init__.py:157 +#: modules/mumble/__init__.py:158 msgid "Mumble server is configured" msgstr "Mumble 服务器已配置" @@ -6330,7 +6330,7 @@ msgid "" "existing calendars and address books." msgstr "" -#: modules/radicale/manifest.py:91 +#: modules/radicale/manifest.py:91 modules/sogo/manifest.py:73 msgid "Calendar" msgstr "" @@ -7252,10 +7252,6 @@ msgstr "" msgid "Webmail" msgstr "" -#: modules/sogo/manifest.py:73 -msgid "Calender" -msgstr "" - #: modules/sogo/manifest.py:74 msgid "Address book" msgstr "地址簿" @@ -8020,27 +8016,27 @@ msgid "" "manually run it now." msgstr "" -#: modules/upgrades/__init__.py:242 +#: modules/upgrades/__init__.py:230 +msgid "Distribution update started" +msgstr "已启动分发更新" + +#: modules/upgrades/__init__.py:232 +msgid "" +"Started update to next stable release. This may take a long time to complete." +msgstr "" + +#: modules/upgrades/__init__.py:256 msgid "Could not start distribution update" msgstr "" -#: modules/upgrades/__init__.py:244 +#: modules/upgrades/__init__.py:258 msgid "" "There is not enough free space in the root partition to start the " "distribution update. Please ensure at least 5 GB is free. Distribution " "update will be retried after 24 hours, if enabled." msgstr "" -#: modules/upgrades/__init__.py:255 -msgid "Distribution update started" -msgstr "已启动分发更新" - -#: modules/upgrades/__init__.py:257 -msgid "" -"Started update to next stable release. This may take a long time to complete." -msgstr "" - -#: modules/upgrades/__init__.py:350 +#: modules/upgrades/__init__.py:352 msgid "Check for package holds" msgstr "" @@ -9078,14 +9074,6 @@ msgstr "安装" msgid "Service %(service_name)s is not running." msgstr "服务 %(service_name)s 未在运行。" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9295,6 +9283,14 @@ msgstr "" msgid "Update" msgstr "更新" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 msgid "Backup" msgstr "备份" @@ -9323,11 +9319,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "设置未改变" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/locale/zh_Hant/LC_MESSAGES/django.po b/plinth/locale/zh_Hant/LC_MESSAGES/django.po index a0bdfba75..ac36f9992 100644 --- a/plinth/locale/zh_Hant/LC_MESSAGES/django.po +++ b/plinth/locale/zh_Hant/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-24 20:13-0500\n" +"POT-Creation-Date: 2025-03-10 20:08-0400\n" "PO-Revision-Date: 2025-02-07 12:01+0000\n" "Last-Translator: pesder \n" "Language-Team: Chinese (Traditional Han script) %(service_name)s is not running." msgstr "" -#: templates/apps.html:33 -msgid "Search with tags" -msgstr "" - -#: templates/apps.html:46 -msgid "Clear all tags" -msgstr "" - #: templates/base.html:31 msgid "" "FreedomBox is a personal server designed for privacy and data ownership. It " @@ -9373,6 +9361,14 @@ msgstr "" msgid "Update" msgstr "" +#: templates/tags.html:24 +msgid "Search with tags" +msgstr "" + +#: templates/tags.html:37 +msgid "Clear all tags" +msgstr "" + #: templates/toolbar.html:39 templates/toolbar.html:40 #, fuzzy #| msgid "Backups" @@ -9405,11 +9401,11 @@ msgstr "" msgid "Here" msgstr "" -#: views.py:376 +#: views.py:414 msgid "Setting unchanged" msgstr "" -#: views.py:610 +#: views.py:647 #, python-brace-format msgid "before uninstall of {app_id}" msgstr "" diff --git a/plinth/modules/bind/__init__.py b/plinth/modules/bind/__init__.py index 213b5b401..ff34d927e 100644 --- a/plinth/modules/bind/__init__.py +++ b/plinth/modules/bind/__init__.py @@ -72,5 +72,6 @@ class BindApp(app_module.App): def force_upgrade(self, _packages): """Force upgrade the managed packages to resolve conffile prompt.""" + # Allow upgrades nay new version by keeping old configuration files install(['bind9'], force_configuration='old') return True diff --git a/plinth/modules/firewall/__init__.py b/plinth/modules/firewall/__init__.py index cc16c659e..038091eb4 100644 --- a/plinth/modules/firewall/__init__.py +++ b/plinth/modules/firewall/__init__.py @@ -88,7 +88,7 @@ class FirewallApp(app_module.App): if 'firewalld' not in packages: return False - # Allow upgrade from any version to any version below 3.0 + # Allow upgrade from 1.3.3 (bookworm) to 2.3.0 (trixie) and beyond 2.x. package = packages['firewalld'] if Version(package['new_version']) > Version('3~'): return False diff --git a/plinth/modules/minidlna/__init__.py b/plinth/modules/minidlna/__init__.py index 2d27309ee..47d9383ce 100644 --- a/plinth/modules/minidlna/__init__.py +++ b/plinth/modules/minidlna/__init__.py @@ -109,7 +109,8 @@ class MiniDLNAApp(app_module.App): if 'minidlna' not in packages: return False - # Allow upgrade from 1.2.1+dfsg-1+b1 to 1.3.x + # Allow upgrade from 1.3.0 (bookworm) to 1.3.3 (trixie) and beyond + # 1.3.x. package = packages['minidlna'] if Version(package['new_version']) > Version('1.4~'): return False diff --git a/plinth/modules/mumble/__init__.py b/plinth/modules/mumble/__init__.py index a7ebcf310..ad7592f13 100644 --- a/plinth/modules/mumble/__init__.py +++ b/plinth/modules/mumble/__init__.py @@ -106,7 +106,8 @@ class MumbleApp(app_module.App): if 'mumble-server' not in packages: return False - # Allow upgrades within 1.3.* + # Allow upgrades from 1.3.4-1 (bullseye) to 1.3.4-4 (bookworm) but not + # to 1.5.735 (trixie) and beyond. package = packages['mumble-server'] if Version(package['new_version']) > Version('1.4~'): return False diff --git a/plinth/modules/radicale/__init__.py b/plinth/modules/radicale/__init__.py index cc312ac98..3ba77f71c 100644 --- a/plinth/modules/radicale/__init__.py +++ b/plinth/modules/radicale/__init__.py @@ -110,7 +110,7 @@ class RadicaleApp(app_module.App): if 'radicale' not in packages: return False - # Allow upgrade from 2.* to newer 2.* and 3.* + # Allow upgrade from 3.1.8 (bookworm) to 3.4.1 (trixie) and beyond 3.x. package = packages['radicale'] if Version(package['new_version']) > Version('4~'): return False diff --git a/plinth/modules/roundcube/__init__.py b/plinth/modules/roundcube/__init__.py index cc3ab1040..0287064a5 100644 --- a/plinth/modules/roundcube/__init__.py +++ b/plinth/modules/roundcube/__init__.py @@ -111,7 +111,8 @@ class RoundcubeApp(app_module.App): return False # Allow roundcube any version to upgrade to any version. This is okay - # because there will no longer be conflicting file changes. + # because there will no longer be conflicting file changes after + # upgrade from bullseye to bookworm. install(['roundcube-core'], force_configuration='new') if self.get_component('webserver-roundcube').is_enabled(): self.get_component('webserver-roundcube-freedombox').enable() diff --git a/plinth/modules/sogo/manifest.py b/plinth/modules/sogo/manifest.py index ed0cb11d1..9e1e0a0c6 100644 --- a/plinth/modules/sogo/manifest.py +++ b/plinth/modules/sogo/manifest.py @@ -70,7 +70,7 @@ backup = { tags = [ _('Webmail'), _('Groupware'), - _('Calender'), + _('Calendar'), _('Address book'), _('CalDAV'), _('CardDAV') diff --git a/plinth/modules/upgrades/__init__.py b/plinth/modules/upgrades/__init__.py index 12f532e9e..737371419 100644 --- a/plinth/modules/upgrades/__init__.py +++ b/plinth/modules/upgrades/__init__.py @@ -221,9 +221,23 @@ def try_start_dist_upgrade(test=False): """Try to start dist upgrade.""" from plinth.notification import Notification - result = privileged.start_dist_upgrade(test) - dist_upgrade_started = result['dist_upgrade_started'] - reason = result['reason'] + try: + privileged.start_dist_upgrade(test, _log_error=False) + except RuntimeError as exception: + reason = exception.args[0] + else: + logger.info('Started dist upgrade.') + title = gettext_noop('Distribution update started') + message = gettext_noop( + 'Started update to next stable release. This may take a long ' + 'time to complete.') + Notification.update_or_create(id='upgrades-dist-upgrade-started', + app_id='upgrades', severity='info', + title=title, message=message, actions=[{ + 'type': 'dismiss' + }], group='admin') + return + if 'found-previous' in reason: logger.info( 'Found previous dist-upgrade. If it was interrupted, it will ' @@ -250,20 +264,8 @@ def try_start_dist_upgrade(test=False): title=title, message=message, actions=[{ 'type': 'dismiss' }], group='admin') - elif 'started-dist-upgrade' in reason: - logger.info('Started dist upgrade.') - title = gettext_noop('Distribution update started') - message = gettext_noop( - 'Started update to next stable release. This may take a long ' - 'time to complete.') - Notification.update_or_create(id='upgrades-dist-upgrade-started', - app_id='upgrades', severity='info', - title=title, message=message, actions=[{ - 'type': 'dismiss' - }], group='admin') else: - logger.warning('Unhandled result of start-dist-upgrade: %s, %s', - dist_upgrade_started, reason) + logger.warning('Unhandled result of start-dist-upgrade: %s', reason) def is_backports_requested(): diff --git a/plinth/modules/upgrades/distupgrade.py b/plinth/modules/upgrades/distupgrade.py new file mode 100644 index 000000000..5d4f346d0 --- /dev/null +++ b/plinth/modules/upgrades/distupgrade.py @@ -0,0 +1,343 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +"""Perform distribution upgrade.""" + +import contextlib +import logging +import pathlib +import subprocess +import time +from typing import Generator + +import augeas + +from plinth import action_utils +from plinth.modules import snapshot as snapshot_module + +from . import utils + +logger = logging.getLogger(__name__) + +OBSOLETE_PACKAGES: list[str] = [] + +PACKAGES_WITH_PROMPTS = ['firewalld', 'minidlna', 'radicale'] + +PRE_DEBCONF_SELECTIONS: list[str] = [ + # Tell grub-pc to continue without installing grub again. + 'grub-pc grub-pc/install_devices_empty boolean true' +] + +sources_list = pathlib.Path('/etc/apt/sources.list') +temp_sources_list = pathlib.Path('/etc/apt/sources.list.fbx-dist-upgrade') + + +def _apt_run(arguments): + """Run an apt command and ensure that output is written to stdout.""" + return action_utils.run_apt_command(arguments, stdout=None) + + +def _sources_list_update(old_codename: str, new_codename: str): + """Change the distribution in /etc/apt/sources.list.""" + logger.info('Upgrading from %s to %s...', old_codename, new_codename) + aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + + augeas.Augeas.NO_MODL_AUTOLOAD) + aug.transform('aptsources', str(sources_list)) + aug.set('/augeas/context', '/files' + str(sources_list)) + aug.set('/augeas/save', 'newfile') # Save to a new file + aug.load() + + for match_ in aug.match('*'): + dist_path = match_ + '/distribution' + dist = aug.get(dist_path) + if dist in (old_codename, 'stable'): + aug.set(dist_path, new_codename) + elif dist and (dist.startswith(old_codename + '-') + or dist.startswith('stable' + '-')): + new_value = new_codename + '-' + dist.partition('-')[2] + aug.set(dist_path, new_value) + + aug.save() + + aug_path = sources_list.with_suffix('.list.augnew') + aug_path.rename(temp_sources_list) + + +def _get_new_codename(test_upgrade: bool) -> str | None: + """Return the codename for the next release.""" + release_file_dist = 'stable' + if test_upgrade: + release_file_dist = 'testing' + + url = utils.RELEASE_FILE_URL.format(release_file_dist) + command = ['curl', '--silent', '--location', '--fail', url] + protocol = utils.get_http_protocol() + if protocol == 'tor+http': + command.insert(0, 'torsocks') + logging.info('Package download over Tor is enabled.') + + try: + output = subprocess.check_output(command).decode() + except (subprocess.CalledProcessError, FileNotFoundError): + logging.warning('Error while checking for new %s release', + release_file_dist) + else: + for line in output.split('\n'): + if line.startswith('Codename:'): + return line.split()[1] + + return None + + +def _check(test_upgrade: bool = False) -> tuple[str, str]: + """Check if a distribution upgrade be performed. + + Check for new stable release, if updates are enabled, and if there is + enough free space for the dist upgrade. + + If test_upgrade is True, also check for upgrade to testing. + + Return (boolean, string) indicating if the upgrade is ready, and a reason + if not. + """ + if not utils.check_auto(): + raise RuntimeError('upgrades-not-enabled') + + if not utils.is_sufficient_free_space(): + raise RuntimeError('not-enough-free-space') + + if action_utils.service_is_running('freedombox-dist-upgrade'): + raise RuntimeError('found-previous') + + from plinth.modules.upgrades import get_current_release + release, old_codename = get_current_release() + if release in ['unstable', 'testing', 'n/a']: + raise RuntimeError(f'already-{release}') + + new_codename = _get_new_codename(test_upgrade) + if not new_codename: + raise RuntimeError('codename-not-found') + + if new_codename == old_codename: + raise RuntimeError(f'already-{old_codename}') + + return old_codename, new_codename + + +@contextlib.contextmanager +def _snapshot_run_and_disable() -> Generator[None, None, None]: + """Take a snapshot if supported and enabled, then disable snapshots. + + Snapshots shall be re-enabled, if originally enabled, on exiting this + context manager.. + """ + if not snapshot_module.is_supported(): + logger.info('Snapshots are not supported, skipping taking a snapshot.') + yield + return + + reenable = False + try: + logger.info('Taking a snapshot before dist upgrade...') + subprocess.run([ + '/usr/share/plinth/actions/actions', 'snapshot', 'create', + '--no-args' + ], check=True) + aug = snapshot_module.load_augeas() + if snapshot_module.is_apt_snapshots_enabled(aug): + logger.info('Disabling apt snapshots during dist upgrade...') + subprocess.run([ + '/usr/share/plinth/actions/actions', + 'snapshot', + 'disable_apt_snapshot', + ], input='{"args": ["yes"], "kwargs": {}}'.encode(), check=True) + reenable = True + else: + logger.info('Apt snapshots already disabled.') + + yield + finally: + if reenable: + logger.info('Re-enabling apt snapshots...') + subprocess.run([ + '/usr/share/plinth/actions/actions', 'snapshot', + 'disable_apt_snapshot' + ], input='{"args": ["no"], "kwargs": {}}'.encode(), check=True) + else: + logger.info('Not re-enabling apt snapshots, as they were disabled ' + 'before dist upgrade.') + + +@contextlib.contextmanager +def _services_disable(): + """Disable services that are seriously impacted by the upgrade.""" + # If quassel is running during dist upgrade, it may be restarted + # several times. This causes IRC users to rapidly leave/join + # channels. Stop quassel for the duration of the dist upgrade. + logger.info('Stopping quassel service during dist upgrade...') + with action_utils.service_ensure_stopped('quasselcore'): + yield + logger.info('Re-enabling quassel service if previously enabled...') + + +@contextlib.contextmanager +def _apt_hold_packages(): + """Apt hold some packages during dist upgrade.""" + packages = PACKAGES_WITH_PROMPTS + packages_string = ', '.join(packages) + + # Hold freedombox package during entire dist upgrade. + logger.info('Holding freedombox package...') + with action_utils.apt_hold_freedombox(): + # Hold packages known to have conffile prompts. FreedomBox service + # will handle their upgrade later. + logger.info('Holding packages with conffile prompts: %s...', + packages_string) + with action_utils.apt_hold(packages): + yield + logger.info( + 'Releasing holds on packages with conffile prompts: %s...', + packages_string) + + logger.info('Releasing hold on freedombox package...') + + +def _debconf_set_selections() -> None: + """Pre-set debconf selections if they are needed for dist upgrade.""" + if PRE_DEBCONF_SELECTIONS: + logger.info('Setting debconf selections: %s', PRE_DEBCONF_SELECTIONS) + action_utils.debconf_set_selections(PRE_DEBCONF_SELECTIONS) + + +def _packages_remove_obsolete() -> None: + """Remove obsolete packages. + + These may prevent other packages from upgrading. + """ + if OBSOLETE_PACKAGES: + logger.info('Removing packages: %s...', OBSOLETE_PACKAGES) + _apt_run(['remove'] + OBSOLETE_PACKAGES) + + +def _apt_update(): + """Run 'apt update'.""" + logger.info('Updating Apt cache...') + _apt_run(['update']) + + +def _apt_autoremove(): + """Run 'apt autoremove'.""" + logger.info('Running apt autoremove...') + _apt_run(['autoremove']) + + +def _apt_full_upgrade(): + """Run and check if apt upgrade was successful.""" + logger.info('Running apt full-upgrade...') + returncode = _apt_run(['full-upgrade']) + if returncode: + raise RuntimeError( + 'Apt full-upgrade was not successful. Distribution upgrade ' + 'will be retried at a later time.') + + +def _unattended_upgrades_run(): + """Run unattended-upgrade once more. + + To handle upgrading the freedombox package. + """ + logger.info('Running unattended-upgrade...') + subprocess.run(['unattended-upgrade', '--verbose'], check=False) + + +def _freedombox_restart(): + """Restart FreedomBox service. + + To ensure it is using the latest dependencies. + """ + logger.info('Restarting FreedomBox service...') + action_utils.service_restart('plinth') + + +def _wait(): + """Wait for 10 minutes before performing remaining actions.""" + logger.info('Waiting for 10 minutes...') + time.sleep(10 * 60) + + +def _trigger_on_complete(): + """Trigger the on complete step in a separate service.""" + # The dist-upgrade process will be run /etc/apt/sources.list file bind + # mounted on with a modified file. So, moving modified file to the original + # file will not be possible. For that, we need to launch a new process with + # a different systemd service (which does not have the bind mounts). + logger.info('Triggering on-complete to commit sources.lists') + subprocess.run([ + 'systemd-run', '--unit=freedombox-dist-upgrade-on-complete', + '--description=Finish up upgrade to new stable Debian release', + '/usr/share/plinth/actions/actions', 'upgrades', + 'dist_upgrade_on_complete', '--no-args' + ], check=True) + + +def _logging_setup(): + """Log to journal via console logging. + + We need to capture all console logs created by apt and other commands and + redirect them to journal. This is the default behavior when launching a + service with systemd-run. + + Avoid double logging to the journal by removing the systemd journal as a + log handler.. + """ + logging.getLogger(None).removeHandler('journal') + + +def perform(): + """Perform upgrade to next release of Debian.""" + _logging_setup() + with _snapshot_run_and_disable(), \ + _services_disable(), \ + _apt_hold_packages(): + _apt_update() + _debconf_set_selections() + _packages_remove_obsolete() + _apt_full_upgrade() + _apt_autoremove() + + _unattended_upgrades_run() + _freedombox_restart() + _wait() + _apt_update() + _trigger_on_complete() + + +def start_service(test_upgrade: bool): + """Create dist upgrade service and start it.""" + # Cleanup old service + old_service_path = pathlib.Path( + '/run/systemd/system/freedombox-dist-upgrade.service') + if old_service_path.exists(): + old_service_path.unlink(missing_ok=True) + action_utils.service_daemon_reload() + + old_codename, new_codename = _check(test_upgrade) + + _sources_list_update(old_codename, new_codename) + + args = [ + '--unit=freedombox-dist-upgrade', + '--description=Upgrade to new stable Debian release', + '--property=KillMode=process', '--property=TimeoutSec=12hr', + f'--property=BindPaths={temp_sources_list}:{sources_list}' + ] + subprocess.run(['systemd-run'] + args + [ + 'systemd-inhibit', '/usr/share/plinth/actions/actions', 'upgrades', + 'dist_upgrade', '--no-args' + ], check=True) + + +def on_complete(): + """Perform cleanup operations.""" + _logging_setup() + logger.info('Dist upgrade complete.') + logger.info('Committing changes to /etc/apt/sources.list') + temp_sources_list.rename(sources_list) diff --git a/plinth/modules/upgrades/privileged.py b/plinth/modules/upgrades/privileged.py index 46bb37c2f..63737c4f0 100644 --- a/plinth/modules/upgrades/privileged.py +++ b/plinth/modules/upgrades/privileged.py @@ -6,29 +6,21 @@ import os import pathlib import re import subprocess -import time -from plinth.action_utils import (apt_hold, apt_hold_flag, apt_hold_freedombox, - apt_unhold_freedombox, debconf_set_selections, +from plinth.action_utils import (apt_hold_flag, apt_unhold_freedombox, is_package_manager_busy, run_apt_command, - service_daemon_reload, service_is_running, - service_restart, service_start, service_stop) + service_is_running) from plinth.actions import privileged -from plinth.modules.apache.components import check_url -from plinth.modules.snapshot import is_apt_snapshots_enabled -from plinth.modules.snapshot import is_supported as snapshot_is_supported -from plinth.modules.snapshot import load_augeas as snapshot_load_augeas + +from . import distupgrade, utils logger = logging.getLogger(__name__) -SOURCES_LIST = '/etc/apt/sources.list' BACKPORTS_SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list' AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades' LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log' DPKG_LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades-dpkg.log' -RELEASE_FILE_URL = \ - 'https://deb.debian.org/debian/dists/{}/Release' APT_PREFERENCES_FREEDOMBOX = \ '''Explanation: This file is managed by FreedomBox, do not edit. @@ -53,38 +45,6 @@ Pin: release n=bookworm-backports Pin-Priority: 500 ''' -DIST_UPGRADE_OBSOLETE_PACKAGES: list[str] = [] - -DIST_UPGRADE_PACKAGES_WITH_PROMPTS = [ - 'bind9', 'firewalld', 'janus', 'minetest-server', 'minidlna', - 'mumble-server', 'radicale', 'roundcube-core', 'tt-rss' -] - -DIST_UPGRADE_PRE_DEBCONF_SELECTIONS: list[str] = [ - # Tell grub-pc to continue without installing grub again. - 'grub-pc grub-pc/install_devices_empty boolean true' -] - -DIST_UPGRADE_REQUIRED_FREE_SPACE = 5000000 - -DIST_UPGRADE_SERVICE = ''' -[Unit] -Description=Upgrade to new stable Debian release - -[Service] -Type=oneshot -ExecStart=systemd-inhibit /usr/share/plinth/actions/actions \ - upgrades dist_upgrade --no-args -KillMode=process -TimeoutSec=12hr -''' - -DIST_UPGRADE_SERVICE_PATH = \ - '/run/systemd/system/freedombox-dist-upgrade.service' - -dist_upgrade_flag = pathlib.Path( - '/var/lib/freedombox/dist-upgrade-in-progress') - def _release_held_freedombox(): """If freedombox package was left in held state, release it. @@ -127,25 +87,10 @@ def run(): start_new_session=True) -def _check_auto() -> bool: - """Check if automatic upgrades are enabled.""" - arguments = [ - 'apt-config', 'shell', 'UpdateInterval', - 'APT::Periodic::Update-Package-Lists' - ] - output = subprocess.check_output(arguments).decode() - update_interval = 0 - match = re.match(r"UpdateInterval='(.*)'", output) - if match: - update_interval = int(match.group(1)) - - return bool(update_interval) - - @privileged def check_auto() -> bool: """Check if automatic upgrades are enabled.""" - return _check_auto() + return utils.check_auto() @privileged @@ -185,34 +130,6 @@ def get_log() -> str: return '\n'.join(log_lines) -def _get_protocol() -> str: - """Return the protocol to use for newly added repository sources.""" - try: - from plinth.modules.torproxy import utils - if utils.is_apt_transport_tor_enabled(): - return 'tor+http' - except Exception: - pass - - return 'http' - - -def _is_release_file_available(protocol: str, dist: str, - backports=False) -> bool: - """Return whether the release for dist[-backports] is available.""" - wrapper = None - if protocol == 'tor+http': - wrapper = 'torsocks' - - if backports: - dist += '-backports' - - try: - return check_url(RELEASE_FILE_URL.format(dist), wrapper=wrapper) - except FileNotFoundError: - return False - - def _add_backports_sources(sources_list: str, protocol: str, dist: str): """Add backports sources to freedombox repositories list.""" sources = '''# This file is managed by FreedomBox, do not edit. @@ -260,11 +177,11 @@ def _check_and_backports_sources(develop=False): logging.info(f'System release is {release}. Skip enabling backports.') return - protocol = _get_protocol() + protocol = utils.get_http_protocol() if protocol == 'tor+http': logging.info('Package download over Tor is enabled.') - if not _is_release_file_available(protocol, dist, backports=True): + if not utils.is_release_file_available(protocol, dist, backports=True): logging.info( f'Release file for {dist}-backports is not available yet.') return @@ -302,244 +219,6 @@ def _add_apt_preferences(): file_handle.write(APT_PREFERENCES_APPS) -def _is_sufficient_free_space() -> bool: - """Return whether there is sufficient free space for dist upgrade.""" - output = subprocess.check_output(['df', '--output=avail', '/']) - free_space = int(output.decode().split('\n')[1]) - return free_space >= DIST_UPGRADE_REQUIRED_FREE_SPACE - - -def _check_dist_upgrade(test_upgrade=False) -> tuple[bool, str]: - """Check if a distribution upgrade be performed. - - Check for new stable release, if updates are enabled, and if there is - enough free space for the dist upgrade. - - If test_upgrade is True, also check for upgrade to testing. - - Return (boolean, string) indicating if the upgrade is ready, and a reason - if not. - """ - if dist_upgrade_flag.exists(): - return (True, 'found-previous') - - from plinth.modules.upgrades import get_current_release - release, dist = get_current_release() - if release in ['unstable', 'testing', 'n/a']: - return (False, f'already-{release}') - - check_dists = ['stable'] - if test_upgrade: - check_dists.append('testing') - - codename = None - for check_dist in check_dists: - url = RELEASE_FILE_URL.format(check_dist) - command = ['curl', '--silent', '--location', '--fail', url] - protocol = _get_protocol() - if protocol == 'tor+http': - command.insert(0, 'torsocks') - logging.info('Package download over Tor is enabled.') - - try: - output = subprocess.check_output(command).decode() - except (subprocess.CalledProcessError, FileNotFoundError): - logging.warning('Error while checking for new %s release', - check_dist) - else: - for line in output.split('\n'): - if line.startswith('Codename:'): - codename = line.split()[1] - - if not codename: - return (False, 'codename-not-found') - - if codename == dist: - return (False, f'already-{dist}') - - if not _check_auto(): - return (False, 'upgrades-not-enabled') - - if check_dist == 'testing' and not test_upgrade: - return (False, 'test-not-set') - - if not _is_sufficient_free_space(): - return (False, 'not-enough-free-space') - - logging.info('Upgrading from %s to %s...', dist, codename) - with open(SOURCES_LIST, 'r', encoding='utf-8') as sources_list: - lines = sources_list.readlines() - - with open(SOURCES_LIST, 'w', encoding='utf-8') as sources_list: - for line in lines: - # E.g. replace 'bullseye' with 'bookworm'. - new_line = line.replace(dist, codename) - if check_dist == 'testing': - # E.g. replace 'stable' with 'bookworm'. - new_line = new_line.replace('stable', codename) - - sources_list.write(new_line) - - logging.info('Dist upgrade in progress. Setting flag.') - dist_upgrade_flag.touch(mode=0o660) - return (True, 'started-dist-upgrade') - - -def _take_snapshot_and_disable() -> bool: - """Take a snapshot if supported and enabled, then disable snapshots. - - Return whether snapshots shall be re-enabled at the end. - """ - if snapshot_is_supported(): - print('Taking a snapshot before dist upgrade...', flush=True) - subprocess.run([ - '/usr/share/plinth/actions/actions', 'snapshot', 'create', - '--no-args' - ], check=True) - aug = snapshot_load_augeas() - if is_apt_snapshots_enabled(aug): - print('Disable apt snapshots during dist upgrade...', flush=True) - subprocess.run([ - '/usr/share/plinth/actions/actions', - 'snapshot', - 'disable_apt_snapshot', - ], input='{"args": ["yes"], "kwargs": {}}'.encode(), check=True) - return True - else: - print('Apt snapshots already disabled.', flush=True) - else: - print('Snapshots are not supported, skip taking a snapshot.', - flush=True) - - return False - - -def _restore_snapshots_config(reenable=False): - """Restore original snapshots configuration.""" - if reenable: - print('Re-enable apt snapshots...', flush=True) - subprocess.run([ - '/usr/share/plinth/actions/actions', 'snapshot', - 'disable_apt_snapshot' - ], input='{"args": ["no"], "kwargs": {}}'.encode(), check=True) - - -def _disable_searx() -> bool: - """If searx is enabled, disable it until we can upgrade it properly. - - Return whether searx was originally enabled. - """ - searx_is_enabled = pathlib.Path( - '/etc/uwsgi/apps-enabled/searx.ini').exists() - if searx_is_enabled: - print('Disabling searx...', flush=True) - subprocess.run( - ['/usr/share/plinth/actions/actions', 'apache', 'uwsgi_disable'], - input='{"args": ["searx"], "kwargs": {}}'.encode(), check=True) - - return searx_is_enabled - - -def _update_searx(reenable=False): - """If searx is installed, update search engines list. - - Re-enable if previously enabled. - """ - if pathlib.Path('/etc/searx/settings.yml').exists(): - print('Updating searx search engines list...', flush=True) - subprocess.run([ - '/usr/share/plinth/actions/actions', 'searx', 'setup', '--no-args' - ], check=True) - if reenable: - print('Re-enabling searx after upgrade...', flush=True) - subprocess.run([ - '/usr/share/plinth/actions/actions', 'apache', 'uwsgi_enable' - ], input='{"args": ["searx"], "kwargs": {}}'.encode(), check=True) - - -def _perform_dist_upgrade(): - """Perform upgrade to next release of Debian.""" - reenable_snapshots = _take_snapshot_and_disable() - reenable_searx = _disable_searx() - - # If quassel is running during dist upgrade, it may be restarted - # several times. This causes IRC users to rapidly leave/join - # channels. Stop quassel for the duration of the dist upgrade. - quassel_service = 'quasselcore' - quassel_was_running = service_is_running(quassel_service) - if quassel_was_running: - print('Stopping quassel service before dist upgrade...', flush=True) - service_stop(quassel_service) - - # Hold freedombox package during entire dist upgrade. - print('Holding freedombox package...', flush=True) - with apt_hold_freedombox(): - print('Updating Apt cache...', flush=True) - run_apt_command(['update']) - - # Pre-set debconf selections if they are required during the - # dist upgrade. - if DIST_UPGRADE_PRE_DEBCONF_SELECTIONS: - print( - f'Setting debconf selections: ' - f'{DIST_UPGRADE_PRE_DEBCONF_SELECTIONS}', flush=True) - debconf_set_selections(DIST_UPGRADE_PRE_DEBCONF_SELECTIONS) - - # Remove obsolete packages that may prevent other packages from - # upgrading. - if DIST_UPGRADE_OBSOLETE_PACKAGES: - print(f'Removing packages: {DIST_UPGRADE_OBSOLETE_PACKAGES}...', - flush=True) - run_apt_command(['remove'] + DIST_UPGRADE_OBSOLETE_PACKAGES) - - # Hold packages known to have conffile prompts. FreedomBox service - # will handle their upgrade later. - print( - 'Holding packages with conffile prompts: ' + - ', '.join(DIST_UPGRADE_PACKAGES_WITH_PROMPTS) + '...', flush=True) - with apt_hold(DIST_UPGRADE_PACKAGES_WITH_PROMPTS): - print('Running apt full-upgrade...', flush=True) - returncode = run_apt_command(['full-upgrade']) - - # Check if apt upgrade was successful. - if returncode: - raise RuntimeError( - 'Apt full-upgrade was not successful. Distribution upgrade ' - 'will be retried at a later time.') - - _update_searx(reenable_searx) - - if quassel_was_running: - print('Re-starting quassel service after dist upgrade...', - flush=True) - service_start(quassel_service) - - print('Running apt autoremove...', flush=True) - run_apt_command(['autoremove']) - - # Run unattended-upgrade once more to handle upgrading the - # freedombox package. - print('Running unattended-upgrade...', flush=True) - subprocess.run(['unattended-upgrade', '--verbose'], check=False) - - _restore_snapshots_config(reenable_snapshots) - - # Restart FreedomBox service to ensure it is using the latest - # dependencies. - print('Restarting FreedomBox service...', flush=True) - service_restart('plinth') - - # After 10 minutes, update apt cache again to trigger force_upgrades. - print('Waiting for 10 minutes...', flush=True) - time.sleep(10 * 60) - print('Updating Apt cache...', flush=True) - run_apt_command(['update']) - - print('Dist upgrade complete. Removing flag.', flush=True) - if dist_upgrade_flag.exists(): - dist_upgrade_flag.unlink() - - @privileged def setup(): """Setup apt preferences.""" @@ -556,21 +235,8 @@ def activate_backports(develop: bool = False): _check_and_backports_sources(develop) -def _start_dist_upgrade_service(): - """Create dist upgrade service and start it.""" - with open(DIST_UPGRADE_SERVICE_PATH, 'w', - encoding='utf-8') as service_file: - service_file.write(DIST_UPGRADE_SERVICE) - - service_daemon_reload() - subprocess.Popen(['systemctl', 'start', 'freedombox-dist-upgrade'], - stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, close_fds=True, - start_new_session=True) - - @privileged -def start_dist_upgrade(test: bool = False) -> dict[str, str | bool]: +def start_dist_upgrade(test: bool = False): """Start dist upgrade process. Check if a new stable release is available, and start dist-upgrade process @@ -578,14 +244,16 @@ def start_dist_upgrade(test: bool = False) -> dict[str, str | bool]: """ _release_held_freedombox() - upgrade_ready, reason = _check_dist_upgrade(test) - if upgrade_ready: - _start_dist_upgrade_service() - - return {'dist_upgrade_started': upgrade_ready, 'reason': reason} + distupgrade.start_service(test) @privileged def dist_upgrade(): """Perform major distribution upgrade.""" - _perform_dist_upgrade() + distupgrade.perform() + + +@privileged +def dist_upgrade_on_complete(): + """Perform cleanup operations after distribution upgrade.""" + distupgrade.on_complete() diff --git a/plinth/modules/upgrades/tests/test_distupgrade.py b/plinth/modules/upgrades/tests/test_distupgrade.py new file mode 100644 index 000000000..b1e2dedae --- /dev/null +++ b/plinth/modules/upgrades/tests/test_distupgrade.py @@ -0,0 +1,350 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +Test various part of the dist upgrade process. +""" + +import re +import subprocess +from unittest.mock import call, patch + +import pytest + +from plinth.modules.upgrades import distupgrade + +# pylint: disable=protected-access + + +@patch('subprocess.run') +def test_apt_run(run): + """Test that running apt command logs properly.""" + run.return_value.returncode = 10 + args = ['command', 'arg1', 'arg2'] + assert distupgrade._apt_run(args) == 10 + assert run.call_args.args == \ + (['apt-get', '--assume-yes', '--quiet=2'] + args,) + assert not run.call_args.kwargs['stdout'] + + +def test_sources_list_update(tmp_path): + """Test that updating a sources file works.""" + original = ''' +# This is a comment with 'bookworm' in it. +deb http://deb.debian.org/debian bookworm main non-free-firmware +deb-src http://deb.debian.org/debian bookworm main non-free-firmware + +deb http://deb.debian.org/debian bookworm-updates main non-free-firmware +deb-src http://deb.debian.org/debian bookworm-updates main non-free-firmware + +deb http://security.debian.org/debian-security/ bookworm-security main non-free-firmware +deb-src http://security.debian.org/debian-security/ bookworm-security main non-free-firmware + +deb https://deb.debian.org/debian other main +deb https://deb.debian.org/debian bookwormish main +''' # noqa: E501 + modified = ''' +# This is a comment with 'bookworm' in it. +deb http://deb.debian.org/debian trixie main non-free-firmware +deb-src http://deb.debian.org/debian trixie main non-free-firmware + +deb http://deb.debian.org/debian trixie-updates main non-free-firmware +deb-src http://deb.debian.org/debian trixie-updates main non-free-firmware + +deb http://security.debian.org/debian-security/ trixie-security main non-free-firmware +deb-src http://security.debian.org/debian-security/ trixie-security main non-free-firmware + +deb https://deb.debian.org/debian other main +deb https://deb.debian.org/debian bookwormish main +''' # noqa: E501 + + sources_list = tmp_path / 'sources.list' + temp_sources_list = tmp_path / 'sources.list.fbx-dist-upgrade' + + module = 'plinth.modules.upgrades.distupgrade' + with patch(f'{module}.sources_list', sources_list), \ + patch(f'{module}.temp_sources_list', temp_sources_list): + sources_list.write_text(original) + distupgrade._sources_list_update('bookworm', 'trixie') + + assert temp_sources_list.read_text() == modified + + original = re.sub(r'bookworm([ -])', r'stable\1', original) + sources_list.write_text(original) + distupgrade._sources_list_update('bookworm', 'trixie') + + assert temp_sources_list.read_text() == modified + + +@patch('plinth.modules.upgrades.utils.get_http_protocol') +@patch('subprocess.check_output') +def test_get_new_codename(check_output, get_http_protocol): + """Test that getting a new distro codename works.""" + get_http_protocol.return_value = 'http' + check_output.return_value = b''' +Suite: testing +Codename: trixie +Description: Debian Testing distribution +''' + assert distupgrade._get_new_codename(False) == 'trixie' + check_output.assert_called_with([ + 'curl', '--silent', '--location', '--fail', + 'https://deb.debian.org/debian/dists/stable/Release' + ]) + + assert distupgrade._get_new_codename(True) == 'trixie' + check_output.assert_called_with([ + 'curl', '--silent', '--location', '--fail', + 'https://deb.debian.org/debian/dists/testing/Release' + ]) + + check_output.side_effect = FileNotFoundError('curl not found') + assert not distupgrade._get_new_codename(True) + + +@patch('plinth.modules.upgrades.distupgrade._get_new_codename') +@patch('plinth.modules.upgrades.get_current_release') +@patch('plinth.action_utils.service_is_running') +@patch('plinth.modules.upgrades.utils.is_sufficient_free_space') +@patch('plinth.modules.upgrades.utils.check_auto') +def test_check(check_auto, is_sufficient_free_space, service_is_running, + get_current_release, get_new_codename): + """Test checking for available dist upgrade.""" + check_auto.return_value = False + with pytest.raises(RuntimeError, match='upgrades-not-enabled'): + distupgrade._check() + + check_auto.return_value = True + is_sufficient_free_space.return_value = False + with pytest.raises(RuntimeError, match='not-enough-free-space'): + distupgrade._check() + + is_sufficient_free_space.return_value = True + service_is_running.return_value = True + with pytest.raises(RuntimeError, match='found-previous'): + distupgrade._check() + + service_is_running.return_value = False + for release in ['unstable', 'testing', 'n/a']: + get_current_release.return_value = (release, release) + with pytest.raises(RuntimeError, match=f'already-{release}'): + distupgrade._check() + + get_current_release.return_value = ('12', 'bookworm') + get_new_codename.return_value = None + with pytest.raises(RuntimeError, match='codename-not-found'): + distupgrade._check() + get_new_codename.assert_called_with(False) + + distupgrade._check(True) + get_new_codename.assert_called_with(True) + + get_new_codename.return_value = 'bookworm' + with pytest.raises(RuntimeError, match='already-bookworm'): + distupgrade._check() + + get_new_codename.return_value = 'trixie' + assert distupgrade._check() == ('bookworm', 'trixie') + + +@patch('subprocess.run') +@patch('plinth.modules.snapshot.is_apt_snapshots_enabled') +@patch('plinth.modules.snapshot.is_supported') +def test_snapshot_run_and_disable(is_supported, is_apt_snapshots_enabled, run): + """Test taking a snapshot.""" + is_supported.return_value = False + with distupgrade._snapshot_run_and_disable(): + run.assert_not_called() + + run.assert_not_called() + + is_supported.return_value = True + is_apt_snapshots_enabled.return_value = False + with distupgrade._snapshot_run_and_disable(): + assert run.call_args_list == [ + call([ + '/usr/share/plinth/actions/actions', 'snapshot', 'create', + '--no-args' + ], check=True) + ] + run.reset_mock() + + run.assert_not_called() + + is_supported.return_value = True + is_apt_snapshots_enabled.return_value = True + with distupgrade._snapshot_run_and_disable(): + assert run.call_args_list == [ + call([ + '/usr/share/plinth/actions/actions', 'snapshot', 'create', + '--no-args' + ], check=True), + call([ + '/usr/share/plinth/actions/actions', 'snapshot', + 'disable_apt_snapshot' + ], input=b'{"args": ["yes"], "kwargs": {}}', check=True) + ] + run.reset_mock() + + assert run.call_args_list == [ + call([ + '/usr/share/plinth/actions/actions', 'snapshot', + 'disable_apt_snapshot' + ], input=b'{"args": ["no"], "kwargs": {}}', check=True) + ] + + +@patch('plinth.action_utils.service_enable') +@patch('plinth.action_utils.service_disable') +@patch('plinth.action_utils.service_is_running') +def test_services_disable(service_is_running, service_disable, service_enable): + """Test that disabling services works.""" + service_is_running.return_value = False + with distupgrade._services_disable(): + service_disable.assert_not_called() + + service_enable.assert_not_called() + + service_is_running.return_value = True + with distupgrade._services_disable(): + service_disable.call_args_list = [call('quasselcore')] + + service_enable.call_args_list = [call('quasselcore')] + + +@patch('subprocess.run') +@patch('subprocess.check_call') +@patch('subprocess.check_output') +def test_apt_hold_packages(check_output, check_call, run, tmp_path): + """Test that holding apt packages works.""" + hold_flag = tmp_path / 'flag' + run.return_value.returncode = 0 + with patch('plinth.action_utils.apt_hold_flag', hold_flag), \ + patch('plinth.modules.upgrades.distupgrade.PACKAGES_WITH_PROMPTS', + ['package1', 'package2']): + check_output.return_value = False + with distupgrade._apt_hold_packages(): + assert hold_flag.exists() + assert hold_flag.stat().st_mode & 0o117 == 0 + expected_call = [call(['apt-mark', 'hold', 'freedombox'])] + assert check_call.call_args_list == expected_call + expected_calls = [ + call(['apt-mark', 'hold', 'package1'], check=False), + call(['apt-mark', 'hold', 'package2'], check=False) + ] + assert run.call_args_list == expected_calls + check_call.reset_mock() + run.reset_mock() + + expected_call = [ + call(['apt-mark', 'unhold', 'freedombox'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + check=False) + ] + assert run.call_args_list == expected_call + expected_calls = [ + call(['apt-mark', 'unhold', 'package1']), + call(['apt-mark', 'unhold', 'package2']) + ] + assert check_call.call_args_list == expected_calls + + +@patch('plinth.action_utils.debconf_set_selections') +def test_debconf_set_selections(debconf_set_selections): + """Test that setting debconf selections works.""" + selections = 'plinth.modules.upgrades.distupgrade.PRE_DEBCONF_SELECTIONS' + with patch(selections, []): + distupgrade._debconf_set_selections() + debconf_set_selections.assert_not_called() + + with patch(selections, ['selection1', 'selection2']): + distupgrade._debconf_set_selections() + debconf_set_selections.assert_called_with(['selection1', 'selection2']) + + distupgrade._debconf_set_selections() + debconf_set_selections.assert_called_with( + ['grub-pc grub-pc/install_devices_empty boolean true']) + + +@patch('plinth.modules.upgrades.distupgrade._apt_run') +def test_packages_remove_obsolete(apt_run): + """Test that obsolete packages are removed.""" + distupgrade._packages_remove_obsolete() + apt_run.assert_not_called() # No obsolete package to remove currently. + + with patch('plinth.modules.upgrades.distupgrade.OBSOLETE_PACKAGES', + ['tt-rss', 'searx']): + distupgrade._packages_remove_obsolete() + apt_run.assert_called_with(['remove', 'tt-rss', 'searx']) + + +@patch('plinth.modules.upgrades.distupgrade._apt_run') +def test_apt_update(apt_run): + """Test that apt update works.""" + distupgrade._apt_update() + apt_run.assert_called_with(['update']) + + +@patch('plinth.modules.upgrades.distupgrade._apt_run') +def test_apt_autoremove(apt_run): + """Test that apt autoremove works.""" + distupgrade._apt_autoremove() + apt_run.assert_called_with(['autoremove']) + + +@patch('plinth.modules.upgrades.distupgrade._apt_run') +def test_apt_full_upgrade(apt_run): + """Test that apt full upgrade works.""" + apt_run.return_value = 0 + distupgrade._apt_full_upgrade() + apt_run.assert_called_with(['full-upgrade']) + + apt_run.return_value = 1 + with pytest.raises(RuntimeError): + distupgrade._apt_full_upgrade() + + +@patch('subprocess.run') +def test_unatteneded_upgrades_run(run): + """Test that running unattended upgrades works.""" + distupgrade._unattended_upgrades_run() + run.assert_called_with(['unattended-upgrade', '--verbose'], check=False) + + +@patch('plinth.action_utils.service_restart') +def test_freedombox_restart(service_restart): + """Test that restarting freedombox service works.""" + distupgrade._freedombox_restart() + service_restart.assert_called_with('plinth') + + +@patch('time.sleep') +def test_wait(sleep): + """Test that sleeping works.""" + distupgrade._wait() + sleep.assert_called_with(600) + + +@patch('subprocess.run') +def test_trigger_on_complete(run): + """Test triggering post completion process.""" + distupgrade._trigger_on_complete() + run.assert_called_with([ + 'systemd-run', '--unit=freedombox-dist-upgrade-on-complete', + '--description=Finish up upgrade to new stable Debian release', + '/usr/share/plinth/actions/actions', 'upgrades', + 'dist_upgrade_on_complete', '--no-args' + ], check=True) + + +def test_on_complete(tmp_path): + """Test that /etc/apt/sources.list is committed.""" + sources_list = tmp_path / 'sources.list' + sources_list.write_text('before') + temp_sources_list = tmp_path / 'sources.list.fbx-dist-upgrade' + temp_sources_list.write_text('after') + + module = 'plinth.modules.upgrades.distupgrade' + with patch(f'{module}.sources_list', sources_list), \ + patch(f'{module}.temp_sources_list', temp_sources_list): + distupgrade.on_complete() + assert sources_list.read_text() == 'after' + assert not temp_sources_list.exists() diff --git a/plinth/modules/upgrades/utils.py b/plinth/modules/upgrades/utils.py new file mode 100644 index 000000000..0c1780749 --- /dev/null +++ b/plinth/modules/upgrades/utils.py @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +"""Utilities for regular updates and dist-upgrades.""" + +import re +import subprocess + +from plinth.modules.apache.components import check_url + +RELEASE_FILE_URL = \ + 'https://deb.debian.org/debian/dists/{}/Release' + +DIST_UPGRADE_REQUIRED_FREE_SPACE = 5000000 + + +def check_auto() -> bool: + """Return whether automatic updates are enabled.""" + arguments = [ + 'apt-config', 'shell', 'UpdateInterval', + 'APT::Periodic::Update-Package-Lists' + ] + output = subprocess.check_output(arguments).decode() + update_interval = 0 + match = re.match(r"UpdateInterval='(.*)'", output) + if match: + update_interval = int(match.group(1)) + + return bool(update_interval) + + +def get_http_protocol() -> str: + """Return the protocol to use for newly added repository sources.""" + try: + from plinth.modules.torproxy import utils + if utils.is_apt_transport_tor_enabled(): + return 'tor+http' + except Exception: + pass + + return 'http' + + +def is_release_file_available(protocol: str, dist: str, + backports=False) -> bool: + """Return whether the release for dist[-backports] is available.""" + wrapper = None + if protocol == 'tor+http': + wrapper = 'torsocks' + + if backports: + dist += '-backports' + + try: + return check_url(RELEASE_FILE_URL.format(dist), wrapper=wrapper) + except FileNotFoundError: + return False + + +def is_sufficient_free_space() -> bool: + """Return whether there is sufficient free space for dist upgrade.""" + output = subprocess.check_output(['df', '--output=avail', '/']) + free_space = int(output.decode().split('\n')[1]) + return free_space >= DIST_UPGRADE_REQUIRED_FREE_SPACE diff --git a/plinth/setup.py b/plinth/setup.py index 3ae80ea67..faafc14f9 100644 --- a/plinth/setup.py +++ b/plinth/setup.py @@ -647,9 +647,13 @@ class ForceUpgrader(): # App does not implement force upgrade continue - if (app.get_setup_state() != app_module.App.SetupState.UP_TO_DATE): - # App is not installed. - # Or needs an update, let it update first. + if (app.get_setup_state() == app_module.App.SetupState.NEEDS_SETUP + ): + # If an app is not installed don't considered it. If an app + # needs an update, it may have to do a force upgrade before + # running app version update. This is because the app version + # update process will include installing packages that will + # fail due to pending configuration file updates. continue for component in app.get_components_of_type(Packages): diff --git a/plinth/templates/app-header.html b/plinth/templates/app-header.html index e584a6f2f..efed5c201 100644 --- a/plinth/templates/app-header.html +++ b/plinth/templates/app-header.html @@ -45,7 +45,7 @@ {% if app_info.tags %}
{% for tag in app_info.tags %} - {% trans tag %} diff --git a/plinth/templates/apps.html b/plinth/templates/apps.html index 098be288b..439c36a17 100644 --- a/plinth/templates/apps.html +++ b/plinth/templates/apps.html @@ -11,42 +11,3 @@ {% endblock %} {% block body_class %}apps-page{% endblock %} - -{% block tags %} - {% if tags %} -
- -
- {% endif %} -{% endblock %} diff --git a/plinth/templates/cards.html b/plinth/templates/cards.html index 64ac96354..098b20e36 100644 --- a/plinth/templates/cards.html +++ b/plinth/templates/cards.html @@ -15,7 +15,9 @@
- {% block tags %}{% endblock %} + {% block tags %} + {% include "tags.html" %} + {% endblock %}
diff --git a/plinth/templates/system.html b/plinth/templates/system.html index fbd15a31b..6f573d179 100644 --- a/plinth/templates/system.html +++ b/plinth/templates/system.html @@ -6,6 +6,10 @@ {% load static %} {% load i18n %} +{% block page_js %} + +{% endblock %} + {% block body_class %}system-page{% endblock %} {% block container %} @@ -17,12 +21,16 @@
+ {% block tags %} + {% include "tags.html" %} + {% endblock %} +
{% for section_item in menu_items %}
{{ section_item.name }}
- {% for item in section_item.sorted_items %} + {% for item in section_item.items %} {% if advanced_mode or not item.advanced %} {% include "card.html" %} {% endif %} diff --git a/plinth/templates/tags.html b/plinth/templates/tags.html new file mode 100644 index 000000000..1dd96dfc7 --- /dev/null +++ b/plinth/templates/tags.html @@ -0,0 +1,42 @@ +{% comment %} +# SPDX-License-Identifier: AGPL-3.0-or-later +{% endcomment %} + +{% load i18n %} + +{% if tags %} +
+ +
+{% endif %} diff --git a/plinth/views.py b/plinth/views.py index cbb858689..97128ad1f 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -171,6 +171,79 @@ def index(request): }) +def _pick_menu_items(menu_items, selected_tags): + """Return a sorted list of menu items filtered by tags.""" + + class MenuProxy: + """A proxy for the menu item to hold filtered children.""" + + def __init__(self, menu_item: menu.Menu): + """Initialize a menu proxy object.""" + self.menu_item = menu_item + self.items: list[menu.Menu] = [] + tags = menu_item.tags or [] + for item in menu_item.items: + tags += item.tags or [] + + self.tags = list(tags) + + def __getattr__(self, name: str): + """Return attributed from proxied object.""" + return getattr(self.menu_item, name) + + def _mismatch_map(menu_item) -> list[bool]: + """Return a list of mismatches for selected tags. + + A mismatch is when a selected tag is *not* present in the list of + tags for menu item. + """ + menu_tags = set(menu_item.tags or []) + return [tag not in menu_tags for tag in selected_tags] + + def _sort_key(menu_item): + """Returns a comparable tuple to sort menu items. + + Sort items by tag match count first, then by the order of matched + tags in user specified order, then by the order set by menu item, + and then by the name of the menu item in current locale (by + configured collation order). + """ + return (_mismatch_map(menu_item).count(True), _mismatch_map(menu_item), + menu_item.order, menu_item.name.lower()) + + proxied_menu_items = [] + for menu_item in menu_items: + proxied_item = MenuProxy(menu_item) + proxied_item.items = _pick_menu_items(menu_item.items, selected_tags) + proxied_menu_items.append(proxied_item) + + # Filter out menu items that don't match any of the selected tags. If + # no tags are selected, return all menu items. Otherwise, return all + # menu items that have at least one matching tag. + filtered_menu_items = [ + menu_item for menu_item in proxied_menu_items + if (not selected_tags) or (not all(_mismatch_map(menu_item))) + ] + + return sorted(filtered_menu_items, key=_sort_key) + + +def _get_all_tags(menu_items: list[menu.Menu]) -> list[str]: + """Return a sorted list of all tags present in the given menu items.""" + + def get_tags(menu_items: list[menu.Menu]) -> set[str]: + """Return a list of tags, unsorted.""" + all_tags = set() + for menu_item in menu_items: + all_tags.update(menu_item.tags or []) + all_tags |= get_tags(menu_item.items) + + return all_tags + + # Sort tags by localized string + return sorted(get_tags(menu_items), key=_) + + class AppsIndexView(TemplateView): """View for apps index. @@ -180,41 +253,6 @@ class AppsIndexView(TemplateView): """ template_name = 'apps.html' - @staticmethod - def _pick_menu_items(menu_items, selected_tags): - """Return a sorted list of menu items filtered by tags.""" - - def _mismatch_map(menu_item) -> list[bool]: - """Return a list of mismatches for selected tags. - - A mismatch is when a selected tag is *not* present in the list of - tags for menu item. - """ - menu_tags = set(menu_item.tags) - return [tag not in menu_tags for tag in selected_tags] - - def _sort_key(menu_item): - """Returns a comparable tuple to sort menu items. - - Sort items by tag match count first, then by the order of matched - tags in user specified order, then by the order set by menu item, - and then by the name of the menu item in current locale (by - configured collation order). - """ - return (_mismatch_map(menu_item).count(True), - _mismatch_map(menu_item), menu_item.order, - menu_item.name.lower()) - - # Filter out menu items that don't match any of the selected tags. If - # no tags are selected, return all menu items. Otherwise, return all - # menu items that have at least one matching tag. - filtered_menu_items = [ - menu_item for menu_item in menu_items - if (not selected_tags) or (not all(_mismatch_map(menu_item))) - ] - - return sorted(filtered_menu_items, key=_sort_key) - def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context['show_disabled'] = True @@ -224,24 +262,24 @@ class AppsIndexView(TemplateView): menu_items = menu.main_menu.active_item(self.request).items context['tags'] = tags - # Sorted tags by localized string - all_tags = set() - for menu_item in menu_items: - all_tags.update(menu_item.tags or []) - - context['all_tags'] = sorted(all_tags, key=lambda tag: _(tag)) - context['menu_items'] = self._pick_menu_items(menu_items, tags) + context['all_tags'] = _get_all_tags(menu_items) + context['menu_items'] = _pick_menu_items(menu_items, tags) return context def system_index(request): """Serve the system index page.""" - menu_items = menu.main_menu.active_item(request).sorted_items() - return TemplateResponse(request, 'system.html', { - 'advanced_mode': get_advanced_mode(), - 'menu_items': menu_items - }) + tags = request.GET.getlist('tag', []) + menu_items = menu.main_menu.active_item(request).items + + return TemplateResponse( + request, 'system.html', { + 'advanced_mode': get_advanced_mode(), + 'menu_items': _pick_menu_items(menu_items, tags), + 'tags': tags, + 'all_tags': _get_all_tags(menu_items) + }) class LanguageSelectionView(FormView): @@ -472,9 +510,8 @@ class SetupView(TemplateView): context['setup_state'] = setup_state context['operations'] = operation.manager.filter(app.app_id) context['show_rerun_setup'] = False - context['show_uninstall'] = ( - not app.info.is_essential - and setup_state != app_module.App.SetupState.NEEDS_SETUP) + context['show_uninstall'] = (not app.info.is_essential and setup_state + != app_module.App.SetupState.NEEDS_SETUP) # Perform expensive operation only if needed. if not context['operations']: diff --git a/static/tags.js b/static/tags.js index 100164edc..782d8d46c 100644 --- a/static/tags.js +++ b/static/tags.js @@ -26,15 +26,15 @@ /** * Update the URL path based on the selected tags. * - * If no tags are selected, redirects to the base apps path. Otherwise, + * If no tags are selected, redirects to the base section path. Otherwise, * constructs a new URL with query parameters for each tag. * * @param {string[]} tags - An array of selected tag names. */ function updatePathWithTags(tags) { - const appsPath = window.location.pathname; + const sectionPath = window.location.pathname; if (tags.length === 0) { - this.location.assign(appsPath); + this.location.assign(sectionPath); } else { const urlParams = new URLSearchParams(); tags.forEach(tag => urlParams.append('tag', tag));