diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..894c6ae8e --- /dev/null +++ b/.codespellrc @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: © 2023 Christian Buhtz +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory +# go to +# and . + +[codespell] +# Folders and files to skip +skip = .codespellrc,*.po,Makefile,*.desktop,.git,__pycache__,*.pyc,#*#,languages.py,_build,TRANSLATIONS,./doc/manual/html,mkdocs.yml,configure,.pytest_cache +# Print N lines of surrounding context +context = 1 +# Check hidden files also (empty means True) +check-hidden= +# Print number of errors as last line on stderr (empty means True) +count= + +# Dictionaries to use (default: "clear,rare"). Current: all. +builtin = clear,rare,informal,usage,code,names,en-GB_to_en-US + +# Allowed (ignored) words +ignore-words-list=master,whitelist,manuel,dum,assertIn,OIS,deque,ARCHIV +# Allowed (ignored) words in URLs and URIs +uri-ignore-words-list=mitre,Archiv + +# Good to know about allowed/ignored words: +# Codespell acts a bit unusual when it comes to case-sensitivity. +# By default the word "Manuel" is an error and codespell recommends to +# modify it into "Manual". To allow this German name "Manuel" we have to +# add "manual" (lower case!) to the "ignore-words-list". The upper-case +# version do not work. +# See: https://github.com/codespell-project/codespell/issues/3210 + +# Simulate "# noqa" and ignore all lines with "# codespell-ignore" at the end. +# Credits: https://github.com/codespell-project/codespell/issues/1212#issuecomment-1721152455 +ignore-regex=.*# codespell-ignore$ diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 000000000..93920fe2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,19 @@ +name: Bug report +description: Submit a bug report +body: + - type: markdown + attributes: + value: | + 🌸 The project is maintained by volunteers in their free time, human beings just like you. So be respectful and constructive. + 🚫 **Zero tolerance for AI slop** + 👉 Provide the output of the console command `backintime --diagnostics`, to help us diagnose the problem quickly. + 👉 Please specify as precisely as you can the package or installation source where you got _Back In Time_ from. + 👉 Ask **support questions** in the GNU/Linux community or on our [mailing list](https://mail.python.org/mailman3/lists/bit-dev.python.org). Please respect the limited resources of the maintenance team. + - type: textarea + attributes: + label: "‎" + value: | + 🚫 AI-generated code submissions are not welcome. 🚫 + ⚠️ If you cannot demonstrate full authorship, understanding, and responsibility for your submitted content, your account might be reported to the GitHub/Microsoft abuse team without warning. + + --- diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..ed157ed0e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +- The PR will be closed if you haven't introduced yourself as a first time contributor. +- Read the CONTRIBUTING.md file carefully. +- AI-generated content is strictly prohibited. Accounts submitting such material will be reported for abuse to Microsoft GitHub . +- Provide a screenshot or screencast if you modified the GUI. +- Consider adding a CHANGELOG entry. Run "codespell" to check for typos. diff --git a/.github/REUSE.toml b/.github/REUSE.toml new file mode 100644 index 000000000..19d05271c --- /dev/null +++ b/.github/REUSE.toml @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: © 2024 Back In Time Team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory or go to +# and . + +# See https://reuse.software/faq/#bulk-license +version = 1 + +[[annotations]] +path = ["*.md", "**/*.yml"] +SPDX-License-Identifier = "CC0-1.0" +SPDX-FileCopyrightText = "© 2022 Back In Time" diff --git a/.gitignore b/.gitignore index 2f895b0bc..b741ba8ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,25 @@ +# SPDX-FileCopyrightText: © 2024 Back In Time Team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See folder LICENSES or +# go to +# and . + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class -# editor backup +# editor backup and temp files *~ *.sic +*.swp +\#*\# +.\#* # packaging *.deb @@ -24,16 +38,30 @@ debian/backintime-qt # compressed files *.gz -# Translations +# Compiled translations *.mo # Makefile common/Makefile qt/Makefile -#coveralls.io status +# coveralls.io status .coverage -#sphinx doc build +# coverage result files (raw data and reports) +# .coverage # already ignored, see above +**/htmlcov/** + +# sphinx doc build common/doc-dev/_build/doctrees common/doc-dev/_build/html + +# MkDocs +doc/manual/html/ +CHANGELOG.html + +# PyCharm IDE project settings +.idea + +# Linter configuration +.flake8 diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..87b65e6e5 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: © 2016 Back In Time Team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory or +# go to +# and . +# +# This is the configuration file for the Read the Docs service. +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details. + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3" + jobs: + # Workaround: See PR #1554 for details. + # When migrating to use a pyprojects.toml file switch from this + # workaround to the use of "python: install: extra_requirements..." + # See also: https://docs.readthedocs.io/en/stable/config-file/v2.html#packages + post_create_environment: + - python -m pip install sphinx_rtd_theme +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: common/doc-dev/conf.py + # fail_on_warning: true + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - method: pip +# path: . +# extra_requirements: +# - foo diff --git a/.travis.yml b/.travis.yml index 154276327..473948e7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,57 @@ +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# # TravisCI (https://travis-ci.org) configuration file -# https://docs.travis-ci.com/user/languages/python +os: linux + +# Support End of "Focal" (20.04 LTS) was April 2025 +# dist: focal +# Support End of "Jammy" (22.04 LTS) is April 2027 +# dist: jammy +# Support End of "noble" (24.04 LTS) is April 2029 +dist: noble language: python -# ensures that we have UUID filesystem mounts for proper testing -sudo: true -dist: trusty +arch: + - amd64 +python: + - "3.13" + - "3.14" + addons: # add localhost to known_hosts to prevent ssh unknown host prompt during unit tests ssh_known_hosts: localhost -python: - - "3.4" - - "3.5" - - "3.5-dev" # 3.5 development branch - - "nightly" # currently points to 3.6-dev - +env: + # TravisCI support said this could prevent errors from "make". + PYTHONUNBUFFERED=1 + before_install: # disable mongodb as we don't need it and it sometimes temporary fails # https://github.com/travis-ci/travis-ci/issues/4937#issuecomment-149289729 - - sudo rm -f /etc/apt/sources.list.d/mongodb.list + - sudo rm -f /etc/apt/sources.list.d/mongodb*.list + - sudo apt-key del 90CFB1F5 - sudo apt-get -qq update # install screen, and util-linux (provides flock) for test_sshtools - - sudo apt-get install -y sshfs screen util-linux + - sudo apt-get install -y sshfs screen util-linux libdbus-1-dev + - sudo apt-get install -y ruby rubygems asciidoctor encfs gocryptfs pandoc + - sudo gem install asciidoctor + +jobs: + exclude: + - python: "3.13" install: - - pip install coveralls + - pip install -U pip + - pip install pylint ruff flake8 codespell pyfakefs keyring + - pip install pyqt6 dbus-python # add ssh public / private key pair to ensure user can start ssh session to localhost for tests - ssh-keygen -b 2048 -t rsa -f /home/travis/.ssh/id_rsa -N "" - cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys @@ -34,11 +59,30 @@ install: - eval `ssh-agent -s` script: + # Stop on errors + - set -e + # Add linter versions to build log + - ruff --version + - flake8 --version + - pylint --version + - codespell --version # compile all files - ensure that syntax is correct - - python -m compileall common common/test common/plugins qt qt/plugins - # run unit tests - ensure that functionality is correct - - cd common && ./configure && make unittest-v + - python -m compileall common common/test common/plugins qt qt/test qt/plugins + # common: Install + - cd common + - ./configure + - make + - sudo make install + # common: test + - pytest --verbose + # qt: Install + - cd .. + - cd qt + - ./configure + - make + # qt: Test + - pytest --verbose + # Codespell on whole repo + - cd .. + - codespell -after_success: - - coverage combine - - coveralls diff --git a/AUTHORS b/AUTHORS index 74d7c27fd..ba0083caf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,9 @@ -Oprea Dan () -Bart de Koning () -Richard Bailey () -Germar Reitze () -Taylor Raack (taylor@raack.info) +Oprea Dan +Bart de Koning +Richard Bailey +Germar Reitze +Taylor Raack +Christian Buhtz +Michael Büker +Jürgen Altfeld +…as well as many others involved, whose contributions make this project possible and help it continue to grow and evolve. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..0d2f3b2ac --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1701 @@ + +# Changelog +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) + + +## [2.0.0] (Unreleased Development) + +## Changed + +- **Breaking**: Minimal Python version 3.13 increased +- Changelog migrated to Common Changelog standard +- Build: Changelog shipped as HTML + +## Added +- Build dependency `pandoc` to convert markdown changelog into HTML + +## Fixed +- Prevent Back In Time crash when a plugin fails ([#2447](https://github.com/bit-team/backintime/issues/2447)) + +## [1.6.1] (2026-02-10) + +### Fixed + +- SSH-Key selector widget handle binary keys, missing but configured keys ([#2399](https://github.com/bit-team/backintime/issues/2399), [#2400](https://github.com/bit-team/backintime/issues/2400)) +- SSH-Key selector widget is more robust on unexpected edge cases ([#2399](https://github.com/bit-team/backintime/issues/2399), [#2400](https://github.com/bit-team/backintime/issues/2400)) +- Install backintime-config man page in correct location + +## [1.6.0] (2026-02-08) + +### Changed + +- **Breaking** Disable EncFS for creation of new backup profiles ([#2315](https://github.com/bit-team/backintime/issues/2315), [#1734](https://github.com/bit-team/backintime/issues/1734)) +- **Breaking** A "snapshot" now is a "backup" ([#1929](https://github.com/bit-team/backintime/issues/1929)) +- **Breaking** Deprecated and removed make targets: "test", "test-v", "unittest", "unittest-v" +- Manpage backintime-config moved from section 1 to 5 ([#1773](https://github.com/bit-team/backintime/issues/1773)) +- New dependency "bash" for root mode starter script "backintime-qt_polkit" ([#2328](https://github.com/bit-team/backintime/issues/2328)) +- New dependency (runtime GUI) to "python3-pyqt6.qtsvg" for loading SVG icons ([#1961](https://github.com/bit-team/backintime/issues/1961)) +- Disable scheduling widget in GUI if cron/crontab is missing ([#2245](https://github.com/bit-team/backintime/issues/2245), [@m4rcu5](https://github.com/m4rcu5) Marcus von Dam) +- Expert Options replaced two checkboxes with single widget to configure symlink copying behavior ([#1652](https://github.com/bit-team/backintime/issues/1652)) +- "Repeatedly (anacron)" scheduling behave consistent. Reversed minor bug introduced with 060324e ([#1791](https://github.com/bit-team/backintime/issues/1791)) in 1.5.3 ([#2250](https://github.com/bit-team/backintime/issues/2250)) +- Stricter permissions (600) for fileinfo.bz2 and takesnapshot.log.bz2 ([#2235](https://github.com/bit-team/backintime/issues/2235)) +- Unlocking ssh-agent use "force" instead of "prefer" for SSH_ASKPASS_REQUIRE ([#2170](https://github.com/bit-team/backintime/issues/2170)) ([@daviewales](https://github.com/daviewales)) +- Stop passing window ID when inhibiting suspend via D-Bus power manager ([#2084](https://github.com/bit-team/backintime/issues/2084)) +- Reorder DBUS service provider list for suspend mode inhibition ([#2084](https://github.com/bit-team/backintime/issues/2084)) +- Man pages generated from AsciiDoc, except backintime-config ([#2085](https://github.com/bit-team/backintime/issues/2085)) +- Deprecate command "benchmark-cipher" ([#2120](https://github.com/bit-team/backintime/issues/2120)) +- Deprecate command "snapshots-path" ([#2130](https://github.com/bit-team/backintime/issues/2130)) +- Deprecate commands "snapshots-list", "snapshots-list-path", "last-snapshots" and "last-snapshot-path" ([#2130](https://github.com/bit-team/backintime/issues/2130)) +- Deprecate command "smart-remove" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Deprecate command "backup-job" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Deprecate command "remove-and-do-not-ask-again" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Deprecate command "decode" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Deprecate flag-like command aliases (e.g. "--backup" for "backup") ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Deprecate argument flag "--profile-id" ([#2125](https://github.com/bit-team/backintime/issues/2125)) +- Deprecate argument flag "--share-path" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Deprecate use of SSH Cipher ([#2143](https://github.com/bit-team/backintime/issues/2143)) +- Prefer ed25519 SSH keys (if present) for new created profiles ([#2094](https://github.com/bit-team/backintime/issues/2094)) +- Generating new SSH key will use default key type provided by ssh-keygen ([#2194](https://github.com/bit-team/backintime/issues/2194)) +- Open man page and changelog in a text dialog +- Minimum PyLint version increased to 4.0.0 + +### Added + +- Language Georgian (ka) +- Gocryptfs for local encrypted profiles ([#1897](https://github.com/bit-team/backintime/issues/1897), [#1734](https://github.com/bit-team/backintime/issues/1734), [@germar](https://github.com/germar), [@daviewales](https://github.com/daviewales)) +- Dialog to suggest commonly used files/dirs/patterns for backup exclusion ([#2309](https://github.com/bit-team/backintime/issues/2309)) +- Systray icon can be forced to Dark or Light +- Application logo and symbolic systray icon ([#1961](https://github.com/bit-team/backintime/issues/1961), Gregory Deseck [@gregorydk](https://github.com/gregorydk)) +- Root mode indicator in status bar ([#1964](https://github.com/bit-team/backintime/issues/1964)) +- Option to not using an explicit SSH key file. In consequence this supports extern key agents and SSH clients own configuration ([#1146](https://github.com/bit-team/backintime/issues/1146)) +- SSH-Key selector widget in Manage profiles dialog ([#1146](https://github.com/bit-team/backintime/issues/1146), [#2094](https://github.com/bit-team/backintime/issues/2094), [#2095](https://github.com/bit-team/backintime/issues/2095), [#2275](https://github.com/bit-team/backintime/issues/2275) [@m4rcu5](https://github.com/m4rcu5) Marcus van Dam) +- "ETA" value in status bar ([#2101](https://github.com/bit-team/backintime/issues/2101)) +- Shutdown confirmation dialog ([#2102](https://github.com/bit-team/backintime/issues/2102)) (Huaide Jiang [@LatiosInAltoMare](https://github.com/LatiosInAltoMare)) +- Check and warn if include list entries do not exists in backup source ([#1586](https://github.com/bit-team/backintime/issues/1586)) ([@rafaelhdr](https://github.com/rafaelhdr)) +- Asciidoctor as build dependence ([#2085](https://github.com/bit-team/backintime/issues/2085)) +- Command "show" to replace "snapshots-list", "snapshots-list-path", "last-snapshots" and "last-snapshot-path" ([#2130](https://github.com/bit-team/backintime/issues/2130)) +- Command "prune" to replace "smart-remove" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Flag "--background" for command "backup" to replace command "backup-job" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Flag "--skip-confirmation" for command "remove" to replace command "remove-and-do-not-ask-again" ([#2124](https://github.com/bit-team/backintime/issues/2124)) +- Flag "--profile" accept ID's beside names only ([#2125](https://github.com/bit-team/backintime/issues/2125)) +- Flag "-p" as alias for "--profile" ([#2125](https://github.com/bit-team/backintime/issues/2125)) +- "Less storage space" threshold to warn the user ([#2110](https://github.com/bit-team/backintime/issues/2110)) ([@fest6](https://github.com/fest6)) +- Remember size and position of Manage profiles dialog +- User-callback editor offer a default script in case no script exists ([#1331](https://github.com/bit-team/backintime/issues/1331)) + +### Removed + +- **Breaking** Drop Python support for version 3.9 & 3.10 ([#2129](https://github.com/bit-team/backintime/issues/2129)) +- Stop using QWindow.winId() ([#2084](https://github.com/bit-team/backintime/issues/2084)) +- UI translation in Bosnian, Thai, and Occidental/Interlingue ([#1914](https://github.com/bit-team/backintime/issues/1914)) +- LICENSE file in favor of LICENSES directory and LICENSES.md file +- SSH Cipher configuration in Manage profiles dialog ([#2143](https://github.com/bit-team/backintime/issues/2143)) + +### Fixed + +- Crash in config restore dialog +- Consider symbolic icons as fallbacks ([#2345](https://github.com/bit-team/backintime/issues/2345), [#2289](https://github.com/bit-team/backintime/issues/2289)) +- Warn users and prevent backups to exFAT volumes due to lack of hardlink support ([#2337](https://github.com/bit-team/backintime/issues/2337)) +- Show proper message to users when root mode fails due to inactive or missing polkit agent ([#2328](https://github.com/bit-team/backintime/issues/2328), Derek Veit [@DerekVeit](https://github.com/DerekVeit)) +- Crash in Compare snapshots dialog (aka Snapshots dialog) when comparing/diff two backups ([#2327](https://github.com/bit-team/backintime/issues/2327) Michael Neese [@madic-creates](https://github.com/madic-creates)) +- File view and Compare Backups dialog now open the clicked item correctly even with multiple selection or single-click-to-open enabled ([#2330](https://github.com/bit-team/backintime/issues/2330)) +- Crash in Compare snapshots dialog (aka Snapshots dialog) when comparing/diff two backups ([#2327](https://github.com/bit-team/backintime/issues/2327) Michael Neese [@madic-creates](https://github.com/madic-creates)) +- Enforce UTF-8 encoding in takesnapshot.log and some other files ([#2298](https://github.com/bit-team/backintime/issues/2298)) +- Attribute error about missing 'cbCopyUnsafeLinks' ([#2279](https://github.com/bit-team/backintime/issues/2279)) +- Use LC_ALL instead of LC_TIME to set the locale +- Optimize subparsers and usage output ([#2132](https://github.com/bit-team/backintime/issues/2132)) +- Re-design about dialog ([#1936](https://github.com/bit-team/backintime/issues/1936)) +- Stop waking up monitor when inhibit suspend on backup starts ([#714](https://github.com/bit-team/backintime/issues/714), [#1090](https://github.com/bit-team/backintime/issues/1090)) +- Avoid shutdown confirmation dialog on Budgie and Cinnamon desktop environments ([#788](https://github.com/bit-team/backintime/issues/788)) +- Crash in "Manage profiles" dialog when using "qt6ct" ([#2128](https://github.com/bit-team/backintime/issues/2128)) +- **Breaking** Systray process no longer exposes sensitive backup profile information when the desktop session belongs to another user ([#2237](https://github.com/bit-team/backintime/issues/2237), reported and co-authored by [@samo-sk](https://github.com/samo-sk)) +- Allow in BITs root-mode opening URLs in extern browse + +## [1.5.6] (2025-10-05) + +### Fixed + +- Always use 0 as window ID value when inhibiting suspend via D-Bus power manager ([#2084](https://github.com/bit-team/backintime/issues/2084), [#2268](https://github.com/bit-team/backintime/issues/2268), [#2192](https://github.com/bit-team/backintime/issues/2192)) +- Crash in "Manage profiles" dialog when using "qt6ct" ([#2128](https://github.com/bit-team/backintime/issues/2128)) +- Open online changelog if local CHANGES file is missing ([#2266](https://github.com/bit-team/backintime/issues/2266)) +- Disable opening browser (e.g. project website) in root-mode + +## [1.5.5] (2025-06-05) + +### Fixed + +- Unlocking SSH keys with passphrases on new created profiles ([#2164](https://github.com/bit-team/backintime/issues/2164)) ([@davidfjoh](https://github.com/davidfjoh)) + +## [1.5.4] (2025-03-24) + +### Uncategorized + +- Breaking Change: Auto-remove rules "Free inodes" and "Free space" disabled by default in new created profiles ([#1976](https://github.com/bit-team/backintime/issues/1976)) +- Fix!: Smart-remove rule "Keep one snapshots per week or the last week" use calendar weeks +- Doc: Remove & Retention (formally known as Auto-/Smart-Remove) with improved GUI and user manual section ([#2000](https://github.com/bit-team/backintime/issues/2000)) + +### Changed + +- Completed license information to conform to REUSE.software and SPDX standards. +- More clear and intense warning about EncFS deprecation and removal ([#1904](https://github.com/bit-team/backintime/issues/1904)) +- Updated desktop entry files +- Move several values from config file into new introduce state file ($XDG_STATE_HOME/backintime.json) + +### Fixed + +- Exclude patterns are now case-sensitive when added ([#2040](https://github.com/bit-team/backintime/issues/2040)) +- The width of the fourth column in files view is now saved +- Snapshot compare copy symlink as symlink ([#1902](https://github.com/bit-team/backintime/issues/1902)) (Peter Sevens [@sevens](https://github.com/sevens)) +- Crash when comparing a snapshot with a symlink pointing to a nonexistent target (Peter Sevens [@sevens](https://github.com/sevens)) +- Crash (KeyError) opening language setup dialog with unknown locale/language + +### Added + +- Open user manual (local if available otherwise online) via Help menu +- Toolbar context menu to display the buttons in different combinations with icons and text ([#1105](https://github.com/bit-team/backintime/issues/1105), [#2002](https://github.com/bit-team/backintime/issues/2002)) (Samuel Moore [@s4moore](https://github.com/s4moore)) +- Add offset minutes to hourly schedules (David Gibbs [@fallingrock](https://github.com/fallingrock)) + +## [1.5.3] (2024-11-13) + +### Uncategorized + +- Doc: User manual (build with MkDocs) ([#1838](https://github.com/bit-team/backintime/issues/1838)) (Kosta Vukicevic [@stcksmsh](https://github.com/stcksmsh)) +- Doc: User-callback topic in user manual ([#1659](https://github.com/bit-team/backintime/issues/1659)) +- Refactor!: Remove unused config field "user_callback.no_logging" ([#1887](https://github.com/bit-team/backintime/issues/1887)) +- Refactor!: Remove eCryptFS check for home folder ([#1855](https://github.com/bit-team/backintime/issues/1855)) +- Build: Replace "pycodestyle" linter with "flake8" ([#1839](https://github.com/bit-team/backintime/issues/1839)) + +### Added + +- Support language Interlingua (Occidental) +- Warn if destination directory is formatted as NTFS ([#1854](https://github.com/bit-team/backintime/issues/1854)) (David Gibbs [@fallingrock](https://github.com/fallingrock)) +- Support fcron ([#610](https://github.com/bit-team/backintime/issues/610)) +- User message about release candidate ([#1906](https://github.com/bit-team/backintime/issues/1906)) + +### Fixed + +- Prevent duplicates in Exclude/Include list of Manage Profiles dialog +- Fix Qt segmentation fault when canceling out of unconfigured BiT ([#1095](https://github.com/bit-team/backintime/issues/1095)) (Derek Veit [@DerekVeit](https://github.com/DerekVeit)) +- Correct global flock fallbacks ([#1834](https://github.com/bit-team/backintime/issues/1834)) (Timothy Southwick [@NickNackGus](https://github.com/NickNackGus)) +- Use SSH key password only if it is valid, otherwise request it from user ([#1852](https://github.com/bit-team/backintime/issues/1852)) (David Wales [@daviewales](https://github.com/daviewales)) + +### Changed + +- **Breaking**: Minimal Python version 3.9 required ([#1731](https://github.com/bit-team/backintime/issues/1731)) +- **Breaking**: Auto migration of config version 4 or lower not longer supported ([#1857](https://github.com/bit-team/backintime/issues/1857)) +- Dependency: Remove libnotify-bin (notify-send) ([#1156](https://github.com/bit-team/backintime/issues/1156)) +- Dependency: PyFakeFS minimal version 5.6 ([#1911](https://github.com/bit-team/backintime/issues/1911)) +- General tab and its Schedule section +- Own module for Manage Profiles dialog and separate Generals tab code ([#1865](https://github.com/bit-team/backintime/issues/1865)) +- Remove class OrderedSet +- Remove os.system() from class Execute +- Systray notifications send utilize DBUS instead of notify-send ([#1156](https://github.com/bit-team/backintime/issues/1156)) (Felix Stupp [@Zocker1999NET](https://github.com/Zocker1999NET)) + +## [1.5.2] (2024-08-06) + +### Fixed + +- Ensure crontab with ending newline ([#781](https://github.com/bit-team/backintime/issues/781)) + +### Uncategorized + +- Fix(translation): Correct corrupt translated strings in Basque, Islandic and Spanish causing application crashes ([#1828](https://github.com/bit-team/backintime/issues/1828)) +- Build(translation): Language helper script processing syntax checks on po-files + +## [1.5.1] (2024-07-27) + +### Fixed + +- Use correct port to ping SSH Proxy ([#1815](https://github.com/bit-team/backintime/issues/1815)) + +## [1.5.0] (2024-07-26) + +### Uncategorized + +- Dependency: Migration to PyQt6 +- Breaking Change: EncFS deprecation warning ([#1735](https://github.com/bit-team/backintime/issues/1735), [#1734](https://github.com/bit-team/backintime/issues/1734)) +- Breaking Change: GUI started with --debug does no longer add --debug to the crontab for scheduled profiles. +- Chore!: Remove "debian" folder ([#1548](https://github.com/bit-team/backintime/issues/1548)) +- Build: Enable several PyLint rules ([#1755](https://github.com/bit-team/backintime/issues/1755), [#1766](https://github.com/bit-team/backintime/issues/1766)) +- Build: Add AppStream meta data ([#1642](https://github.com/bit-team/backintime/issues/1642)) +- Build: PyLint unit test is skipped if PyLint isn't installed, but will always run on TravisCI ([#1634](https://github.com/bit-team/backintime/issues/1634)) +- Build: Git commit hash is presevered while "make install" ([#1637](https://github.com/bit-team/backintime/issues/1637)) +- Build: Fix bash-completion symlink creation while installing & adding --diagnostics ([#1615](https://github.com/bit-team/backintime/issues/1615)) +- Build: TravisCI use PyQt (except arch "ppc64le") + +### Added + +- Warn if Cron is not running ([#1747](https://github.com/bit-team/backintime/issues/1747)) +- Profile and GUI allow to activate debug output for scheduled jobs by adding '--debug' to crontab entry ([#1616](https://github.com/bit-team/backintime/issues/1616), contributed by [@stcksmsh](https://github.com/stcksmsh) Kosta Vukicevic) +- Support SSH proxy (jump) host ([#1688](https://github.com/bit-team/backintime/issues/1688)) ([@cgrinham](https://github.com/cgrinham), Christie Grinham) +- Support rsync '--one-file-system' in Expert Options ([#1598](https://github.com/bit-team/backintime/issues/1598)) +- "*-dev" version strings contain last commit hash ([#1637](https://github.com/bit-team/backintime/issues/1637)) + +### Fixed + +- Global flock fallback to single-user mode if insufficient permissions ([#1743](https://github.com/bit-team/backintime/issues/1743), [#1751](https://github.com/bit-team/backintime/issues/1751)) +- Fix Qt segmentation fault with uninstall ExtraMouseButtonEventFilter when closing main window ([#1095](https://github.com/bit-team/backintime/issues/1095)) +- Names of weekdays and months translated correct ([#1729](https://github.com/bit-team/backintime/issues/1729)) +- Global flock for multiple users ([#1122](https://github.com/bit-team/backintime/issues/1122), [#1676](https://github.com/bit-team/backintime/issues/1676)) +- "Backup folders" list does reflect the selected snapshot ([#1585](https://github.com/bit-team/backintime/issues/1585)) ([@rafaelhdr](https://github.com/rafaelhdr) Rafael Hurpia da Rocha) +- Validation of diff command settings in compare snapshots dialog ([#1662](https://github.com/bit-team/backintime/issues/1662)) ([@stcksmsh](https://github.com/stcksmsh) Kosta Vukicevic) +- Open symlinked folders in file view ([#1476](https://github.com/bit-team/backintime/issues/1476)) +- Respect dark mode using color roles ([#1601](https://github.com/bit-team/backintime/issues/1601)) +- "Highly recommended" exclusion pattern in "Manage Profile" dialog's "Exclude" tab show missing only ([#1620](https://github.com/bit-team/backintime/issues/1620)) +- `make install` ignored $(DEST) in file migration part ([#1630](https://github.com/bit-team/backintime/issues/1630)) + +### Removed + +- Context menu in LogViewDialog ([#1578](https://github.com/bit-team/backintime/issues/1578)) +- Field "filesystem_mount" and "snapshot_version" in "info" file ([#1684](https://github.com/bit-team/backintime/issues/1684)) + +### Changed + +- Replace Config.user() with getpass.getuser() ([#1694](https://github.com/bit-team/backintime/issues/1694)) + +## [1.4.3] (2024-01-30) + +### Added + +- Exclude 'SingletonLock' and 'SingletonCookie' (Discord) and 'lock' (Mozilla Firefox) files by default (part of [#1555](https://github.com/bit-team/backintime/issues/1555)) + +### Uncategorized + +- Work around: Relax `rsync` exit code 23: Ignore instead of error now (part of [#1587](https://github.com/bit-team/backintime/issues/1587)) +- Feature (experimental): Add new snapshot log filter `rsync transfer failures (experimental)` to find them easier (they are normally not shown as "error"). +- Improve: Launcher for BiT GUI (root) does not enforce Wayland anymore but uses same settings as for BiT GUI (userland) ([#1350](https://github.com/bit-team/backintime/issues/1350)) +- Change of semantics: BiT running as root never disables suspend during taking a backup ("inhibit suspend") even though this may have worked before in BiT <= v1.4.1 sometimes (required to fix [#1592](https://github.com/bit-team/backintime/issues/1592)) +- Build: Use PyLint in unit testing to catch E1101 (no-member) errors. +- Build: Activate PyLint warning W1401 (anomalous-backslash-in-string). +- Build: Add codespell config. +- Build: Allow manual specification of python executable (--python=PYTHON_PATH) in common/configure and qt/configure +- Build: All starter scripts do use an absolute path to the python executable by default now via common/configure and qt/configure ([#1574](https://github.com/bit-team/backintime/issues/1574)) +- Build: Install dbus configuration file to /usr/share not /etc ([#1596](https://github.com/bit-team/backintime/issues/1596)) +- Build: `configure` does delete old installed files (`qt4plugin.py` and `net.launchpad.backintime.serviceHelper.conf`) that were renamed or moved in a previous release ([#1596](https://github.com/bit-team/backintime/issues/1596)) +- Translation: Minor modifications in source strings and updating language files. +- Improved: qtsystrayicon.py, qt5_probing.py, usercallbackplugin.py and all parts of app.py + +### Fixed + +- 'qt5_probing.py' hangs when BiT is run as root and no user is logged into a desktop environment ([#1592](https://github.com/bit-team/backintime/issues/1592) and [#1580](https://github.com/bit-team/backintime/issues/1580)) +- Launching BiT GUI (root) hangs on Wayland without showing the GUI ([#836](https://github.com/bit-team/backintime/issues/836)) +- Disabling suspend during taking a backup ("inhibit suspend") hangs when BiT is run as root and no user is logged into a desktop environment ([#1592](https://github.com/bit-team/backintime/issues/1592)) +- RTE: module 'qttools' has no attribute 'initate_translator' with encFS when prompting the user for a password ([#1553](https://github.com/bit-team/backintime/issues/1553)). +- Schedule dropdown menu used "minutes" instead of "hours". +- Unhandled exception "TypeError: 'NoneType' object is not callable" in tools.py function __log_keyring_warning ([#820](https://github.com/bit-team/backintime/issues/820)). + +### Changed + +- Solved circular dependency between tools.py and logger.py to fix [#820](https://github.com/bit-team/backintime/issues/820) + +## [1.4.1] (2023-10-01) + +### Uncategorized + +- Dependency: Add "qt translations" to GUI runtime dependencies ([#1538](https://github.com/bit-team/backintime/issues/1538)). +- Build: Unit tests do generically ignore all instead of well-known warnings now ([#1539](https://github.com/bit-team/backintime/issues/1539)). +- Build: Warnings about missing Qt translation now are ignored while testing ([#1537](https://github.com/bit-team/backintime/issues/1537)). + +### Fixed + +- GUI didn't start when "show hidden files" button was on ([#1535](https://github.com/bit-team/backintime/issues/1535)). + +## [1.4.0] (2023-09-14) + +### Uncategorized + +- Project: Renamed branch "master" to "main" and started "gitflow" branching model. +- GUI Change: View last (snapshot) log button in GUI uses "document-open-recent" icon now instead of "document-new" ([#1386](https://github.com/bit-team/backintime/issues/1386)) +- Breaking change: Minimal Python version 3.8 required ([#1358](https://github.com/bit-team/backintime/issues/1358)). +- Documentation: Removed outdated docbook ([#1345](https://github.com/bit-team/backintime/issues/1345)). +- Testing: TravisCI now can use dbus +- Build: Introduced .readthedocs.yaml as asked by ReadTheDocs.org ([#1443](https://github.com/bit-team/backintime/issues/1443)). +- Dependency: The oxygen icons should be installed with the BiT Qt GUI since they are used as fallback in case of missing icons +- Translation: Strings to translate now easier to understand for translators ([#1448](https://github.com/bit-team/backintime/issues/1448), [#1457](https://github.com/bit-team/backintime/issues/1457), [#1462](https://github.com/bit-team/backintime/issues/1462), [#1465](https://github.com/bit-team/backintime/issues/1465)). +- Translation: Improved completeness of translations and additional modifications of source strings ([#1454](https://github.com/bit-team/backintime/issues/1454), [#1512](https://github.com/bit-team/backintime/issues/1512)) +- Translation: Plural forms support ([#1488](https://github.com/bit-team/backintime/issues/1488)). + +### Changed + +- Renamed qt4plugin.py to systrayiconplugin.py (we are using Qt5 for years now ;-) +- Removed unfinished feature "Full system backup" ([#1526](https://github.com/bit-team/backintime/issues/1526)) + +### Fixed + +- AttributeError: can't set attribute 'showHiddenFiles' in app.py ([#1532](https://github.com/bit-team/backintime/issues/1532)) +- Check SSH login works on machines with limited commands ([#1442](https://github.com/bit-team/backintime/issues/1442)) +- Missing icon in SSH private key button ([#1364](https://github.com/bit-team/backintime/issues/1364)) +- Master issue for missing or empty system-tray icon ([#1306](https://github.com/bit-team/backintime/issues/1306)) +- System-tray icon missing or empty (GUI and cron) ([#1236](https://github.com/bit-team/backintime/issues/1236)) +- Improve KDE plasma icon compatibility ([#1159](https://github.com/bit-team/backintime/issues/1159)) +- Unit test fails on some machines due to warning "Ignoring XDG_SESSION_TYPE=wayland on Gnome..." ([#1429](https://github.com/bit-team/backintime/issues/1429)) +- Generation of config-manpage caused an error with Debian's Lintian ([#1398](https://github.com/bit-team/backintime/issues/1398)). +- Return empty list in smartRemove ([#1392](https://github.com/bit-team/backintime/issues/1392), [Debian#973760](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=973760)) +- Taking a snapshot reports `rsync` errors now even if no snapshot was taken ([#1491](https://github.com/bit-team/backintime/issues/1491)) +- takeSnapshot() recognizes errors now by also evaluating the rsync exit code ([#489](https://github.com/bit-team/backintime/issues/489)) +- The error user-callback is now always called if an error happened while taking a snapshot ([#1491](https://github.com/bit-team/backintime/issues/1491)) +- D-Bus serviceHelper error "LimitExceeded: Maximum length of command line reached (100)": +- Treat rsync exit code 24 as INFO instead of ERROR ([#1506](https://github.com/bit-team/backintime/issues/1506)) +- Add support for ChainerBackend class as keyring which iterates over all supported keyring backends ([#1410](https://github.com/bit-team/backintime/issues/1410)) + +### Added + +- Introduce new error codes for the "error" user callback (as part of [#1491](https://github.com/bit-team/backintime/issues/1491)): +- The `rsync` exit code is now contained in the snapshot log (part of [#489](https://github.com/bit-team/backintime/issues/489)). Example: +- Exclude /swapfile by default ([#1053](https://github.com/bit-team/backintime/issues/1053)) +- Rearranged menu bar and its entries in the main window ([#1487](https://github.com/bit-team/backintime/issues/1487), [#1478](https://github.com/bit-team/backintime/issues/1478)). +- Configure user interface language via config file and GUI. +- Translation in Persian and Vietnamese ([#1460](https://github.com/bit-team/backintime/issues/1460)). +- Message to users (after 10 starts of BIT Gui) to motivate them contributing translations ([#1473](https://github.com/bit-team/backintime/issues/1473)). + +### Removed + +- Handling and checking of user group "fuse" ([#1472](https://github.com/bit-team/backintime/issues/1472)). +- Translation in Canadian English, British English and Javanese ([#1455](https://github.com/bit-team/backintime/issues/1455)). + +## [1.3.3] (2023-01-04) + +### Added + +- New command line argument "--diagnostics" to show helpful info for better issue support ([#1100](https://github.com/bit-team/backintime/issues/1100)) +- Write all log output to stderr; do not pollute stdout with INFO and WARNING messages anymore ([#1337](https://github.com/bit-team/backintime/issues/1337)) + +### Uncategorized + +- GUI change: Remove Exit button from the toolbar ([#172](https://github.com/bit-team/backintime/issues/172)) +- GUI change: Define accelerator keys for menu bar and tabs, as well as toolbar shortcuts ([#1104](https://github.com/bit-team/backintime/issues/1104)) +- Desktop integration: Update .desktop file to mark Back In Time as a single main window program ([#1258](https://github.com/bit-team/backintime/issues/1258)) +- Documentation update: Correct description of profile.schedule.time in backintime-config manpage ([#1270](https://github.com/bit-team/backintime/issues/1270)) +- Translation update: Brazilian Portuguese ([#1267](https://github.com/bit-team/backintime/issues/1267)) +- Translation update: Italian ([#1110](https://github.com/bit-team/backintime/issues/1110), [#1123](https://github.com/bit-team/backintime/issues/1123)) +- Translation update: French ([#1077](https://github.com/bit-team/backintime/issues/1077)) +- Testing: Fix a test fail when dealing with an empty crontab ([#1181](https://github.com/bit-team/backintime/issues/1181)) +- Testing: Fix a test fail when dealing with an empty config file ([#1305](https://github.com/bit-team/backintime/issues/1305)) +- Testing: Skip "test_quiet_mode" (does not work reliably) +- Testing: Improve "test_diagnostics_arg" (introduced with [#1100](https://github.com/bit-team/backintime/issues/1100)) to no longer fail +- Testing: Numerous fixes and extensions to testing ([#1115](https://github.com/bit-team/backintime/issues/1115), [#1213](https://github.com/bit-team/backintime/issues/1213), [#1279](https://github.com/bit-team/backintime/issues/1279), [#1280](https://github.com/bit-team/backintime/issues/1280), [#1281](https://github.com/bit-team/backintime/issues/1281), [#1285](https://github.com/bit-team/backintime/issues/1285), [#1288](https://github.com/bit-team/backintime/issues/1288), [#1290](https://github.com/bit-team/backintime/issues/1290), [#1293](https://github.com/bit-team/backintime/issues/1293), [#1309](https://github.com/bit-team/backintime/issues/1309), [#1334](https://github.com/bit-team/backintime/issues/1334)) + +### Fixed + +- RTE "reentrant call inside io.BufferedWriter" in logFile.flush() during backup ([#1003](https://github.com/bit-team/backintime/issues/1003)) +- Incompatibility with rsync 3.2.4 or later because of rsync's "new argument protection" ([#1247](https://github.com/bit-team/backintime/issues/1247)). Deactivate "--old-args" rsync argument earlier recommended to users as a workaround. +- DeprecationWarnings about invalid escape sequences. +- AttributeError in "Diff Options" dialog ([#898](https://github.com/bit-team/backintime/issues/898)) +- Settings GUI: "Save password to Keyring" was disabled due to "no appropriate keyring found" ([#1321](https://github.com/bit-team/backintime/issues/1321)) +- Back in Time did not start with D-Bus error +- Avoid logging errors while waiting for a target drive to be mounted ([#1142](https://github.com/bit-team/backintime/issues/1142), [#1143](https://github.com/bit-team/backintime/issues/1143), [#1328](https://github.com/bit-team/backintime/issues/1328)) +- [Arch Linux] AUR pkg "backintime-git": Build tests fails and installation is aborted ([#1233](https://github.com/bit-team/backintime/issues/1233), fixed with [#921](https://github.com/bit-team/backintime/issues/921)) +- Wrong systray icon showing in Wayland ([#1244](https://github.com/bit-team/backintime/issues/1244)) + +## [1.3.2] (2022-03-12) + +### Fixed + +- Tests no longer work with Python 3.10 ([#1175](https://github.com/bit-team/backintime/issues/1175)) + +## [1.3.1] (2021-07-05) + +### Uncategorized + +- bump version, forgot to push branch to Github before releasing + +## [1.3.0] (2021-07-04) + +### Uncategorized + +- Merge PR: Fix FileNotFoundError exception in mount.mounted, Thanks tatokis ([PR #1157](https://github.com/bit-team/backintime/pull/1157)) +- Merge PR: qt/plugins/notifyplugin: Fix setting self.user, not local variable, Thanks Zocker1999NET ([PR #1155](https://github.com/bit-team/backintime/pull/1155)) +- Merge PR: Use Link Color instead of lightGray as not to break theming, Thanks newhinton ([PR #1153](https://github.com/bit-team/backintime/pull/1153)) +- Merge PR: Match old and new rsync version format, Thanks TheTimeWalker ([PR #1139](https://github.com/bit-team/backintime/pull/1139)) +- Merge PR: 'TempPasswordThread' object has no attribute 'isAlive', Thanks FMeinicke ([PR #1135](https://github.com/bit-team/backintime/pull/1135)) +- Merge PR: Keep permissions of an existing mountpoint from being overridden, Thanks bentolor ([PR #1058](https://github.com/bit-team/backintime/pull/1058)) + +### Fixed + +- YEAR missing in config ([#1023](https://github.com/bit-team/backintime/issues/1023)) +- SSH module didn't send identification string while checking if remote host is available ([#1030](https://github.com/bit-team/backintime/issues/1030)) + +## [1.2.1] (2019-08-25) + +### Fixed + +- TypeError in backintime.py if mount failed while running a snapshot ([#1005](https://github.com/bit-team/backintime/issues/1005)) + +## [1.2.0] (2019-04-27) + +### Fixed + +- Exit code is linked to the wrong status message ([#906](https://github.com/bit-team/backintime/issues/906)) +- AppName showed 'python3' instead of 'Back In Time' ([#950](https://github.com/bit-team/backintime/issues/950)) +- configured cipher is not used with all ssh-commands ([#934](https://github.com/bit-team/backintime/issues/934)) +- 'make test' fails because local SSH server is running on non-standard port ([#945](https://github.com/bit-team/backintime/issues/945)) +- 23:00 is missing in the list of every day hours ([#736](https://github.com/bit-team/backintime/issues/736)) +- ssh-agent output changed ([#840](https://github.com/bit-team/backintime/issues/840)) +- exception on making backintime folder world writable ([#812](https://github.com/bit-team/backintime/issues/812)) +- stat free space for snapshot folder instead of backintime folder ([#733](https://github.com/bit-team/backintime/issues/733)) +- backintime root crontab doesn't run; missing line-feed 0x0A on last line ([#781](https://github.com/bit-team/backintime/issues/781)) +- IndexError in inhibitSuspend ([#772](https://github.com/bit-team/backintime/issues/772)) +- polkit CheckAuthorization: race condition in privilege authorization ([CVE-2017-7572](https://www.cve.org/CVERecord?id=CVE-2017-7572)) +- OSError when running backup-job from systemd ([#720](https://github.com/bit-team/backintime/issues/720)) +- restore filesystem-root without 'Full rsync mode' with ACL and/or xargs activated broke whole system ([#708](https://github.com/bit-team/backintime/issues/708)) +- use current folder if no file is selected in files view ([#687](https://github.com/bit-team/backintime/issues/687), [#685](https://github.com/bit-team/backintime/issues/685)) +- don't reload profile after editing profile name ([#706](https://github.com/bit-team/backintime/issues/706)) +- Exception in FileInfo +- failed to restore suid permissions ([#661](https://github.com/bit-team/backintime/issues/661)) +- on remount user-callback got called AFTER trying to mount ([#654](https://github.com/bit-team/backintime/issues/654)) +- confirm restore dialog has no scroll bar ([#625](https://github.com/bit-team/backintime/issues/625)) +- DEFAULT_EXCLUDE not deletable ([#634](https://github.com/bit-team/backintime/issues/634)) +- GUI status bar unreadable ([#612](https://github.com/bit-team/backintime/issues/612)) +- udev schedule not working ([#605](https://github.com/bit-team/backintime/issues/605)) +- decode path spooled from /etc/mtab ([PR #607](https://github.com/bit-team/backintime/pull/607)) +- in snapshots.py, gives more helpful advice if a lock file is present that shouldn't be. ([#601](https://github.com/bit-team/backintime/issues/601)) +- Fail to create remote snapshot path with spaces ([#567](https://github.com/bit-team/backintime/issues/567)) +- broken new_snapshot can run into infinite saveToContinue loop ([#583](https://github.com/bit-team/backintime/issues/583)) +- udev schedule didn't work with LUKS encrypted drives ([#466](https://github.com/bit-team/backintime/issues/466)) +- sshMaxArg failed on none default ssh port ([#581](https://github.com/bit-team/backintime/issues/581)) +- failed if remote host send SSH banner ([#581](https://github.com/bit-team/backintime/issues/581)) +- incorrect handling of IPv6 addresses ([#577](https://github.com/bit-team/backintime/issues/577)) +- Snapshot Log View freeze on big log files ([#456](https://github.com/bit-team/backintime/issues/456)) +- 'inotify_add_watch failed: file or directory not found' after deleting snapshot +- a continued snapshot was not incremental ([#557](https://github.com/bit-team/backintime/issues/557)) +- config backup in snapshot had wrong name if using --config option +- Can't open files with spaces in name ([#552](https://github.com/bit-team/backintime/issues/552)) +- BIT-root won't start from .desktop file ([#549](https://github.com/bit-team/backintime/issues/549)) +- Keyring doesn't work with KDE Plasma5 ([#545](https://github.com/bit-team/backintime/issues/545)) +- Qt4 built-in phrases where not translated ([Debian#816197](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816197)) +- configure ignore unknown args ([#547](https://github.com/bit-team/backintime/issues/547)) +- snapshots-list on command-line was not sorted +- SHA256 ssh-key fingerprint was not detected +- new snapshot did not show up after finished +- TimeLine headers were not correct +- wildcards ? and [] wasn't recognized correctly +- last char of last element in tools.get_rsync_caps got cut off +- TypeError in tools.get_git_ref_hash +- don't include empty values in list ([#521](https://github.com/bit-team/backintime/issues/521)) +- bash-completion doesn't work for backintime-qt4 +- 'make unittest' incorrectly used 'coverage' by default ([#522](https://github.com/bit-team/backintime/issues/522)) +- pm-utils is deprecated; Remove dependency ([#519](https://github.com/bit-team/backintime/issues/519)) + +### Uncategorized + +- minor changes to allow running BiT inside Docker ([PR #959](https://github.com/bit-team/backintime/pull/959)) +- remove progressbar on systray icon until BiT has it's own icon ([#902](https://github.com/bit-team/backintime/issues/902)) +- clarify 'nocache' option ([#857](https://github.com/bit-team/backintime/issues/857)) +- create a config-backup in root dir if backup is encrypted ([#556](https://github.com/bit-team/backintime/issues/556)) +- move progressbar under statusbar +- alleviate default exclude [Tt]rash* ([#759](https://github.com/bit-team/backintime/issues/759)) +- enable high DPI scaling ([#732](https://github.com/bit-team/backintime/issues/732)) +- Smart Remove try to keep healthy snapshots ([#703](https://github.com/bit-team/backintime/issues/703)) +- ask for restore-to path before confirm ([#678](https://github.com/bit-team/backintime/issues/678)) +- fix 'Back in Time (root)' on wayland ([#640](https://github.com/bit-team/backintime/issues/640)) +- sort int values in config numerical instead if alphabetical ([#175](https://github.com/bit-team/backintime/issues/175)#issuecomment-272941811) +- set timestamp directly after new snapshot ([#584](https://github.com/bit-team/backintime/issues/584)) +- add shortcut CTRL+H for toggle show hidden files to fileselect dialog ([#378](https://github.com/bit-team/backintime/issues/378)) +- redesign restore menu ([#661](https://github.com/bit-team/backintime/issues/661)) +- add ability to disable SSH command- and ping-check ([#647](https://github.com/bit-team/backintime/issues/647)) +- enable bwlimit for local profiles ([#646](https://github.com/bit-team/backintime/issues/646)) +- import remote host-key into known_hosts from Settings +- copy public SSH key to remote host from Settings +- create a new SSH key from Settings +- rename debian package from backintime-qt4 into backintime-qt +- rename paths and methods from *qt4* into *qt* +- rename executable backintime-qt4 into backintime-qt +- new config version 6, rename qt4 keys into qt, add new domain for schedule +- check crontab entries on every GUI startup ([#129](https://github.com/bit-team/backintime/issues/129)) +- start a new ssh-agent instance only if necessary +- add cli command 'shutdown' ([#596](https://github.com/bit-team/backintime/issues/596)) +- make LogView and Settings Dialog non-modal ([#608](https://github.com/bit-team/backintime/issues/608)) +- port to Qt5/pyqt5 ([#518](https://github.com/bit-team/backintime/issues/518)) +- Recognize changes on previous runs while continuing new snapshots +- Add pause, resume and stop function for running snapshots ([#474](https://github.com/bit-team/backintime/issues/474), [#195](https://github.com/bit-team/backintime/issues/195)) +- use rsync to save permissions +- replace os.system calls with subprocess.Popen +- automatically refresh log view if a snapshot is currently running +- make full-rsync mode default, remove the other mode +- use rsync to remove snapshots which will give a nice speedup ([#151](https://github.com/bit-team/backintime/issues/151)) +- open temporary local copy of files instead of original backup on double-click in GUI +- open current log directly from systray icon during taking a snapshot +- use Monospace font in logview +- Fix lintian warning: manpage-has-errors-from-man: bad argument name 'P' +- Do not print 'SnapshotID' or 'SnapshotPath' if running 'snapshots-list' command (and other) with '--quiet' +- Remove dependency 'ps' +- rewrite huge parts of snapshots.py + +### Removed + +- unused and undocumented userscript plugin +- dependency for extended 'find' command on remote host +- backwards compatibility to version < 1.0 + +### Added + +- contextmenu for logview dialog which can copy, exclude and decode lines +- 'Edit user-callback' dialog +- cli command 'smart-remove' +- option to decrypt paths in systray menu with mode ssh-encrypted +- tool-tips to restore menu +- --share-path option +- restore option --only-new +- button 'Take snapshot with checksums' + +### Changed + +- default configure option to --no-fuse-group as Ubuntu >= 12.04 don't need fuse group-membership anymore + +## [1.1.24] (2017-11-07) + +### Fixed + +- shell injection in notify-send ([#834](https://github.com/bit-team/backintime/issues/834), [CVE-2017-16667](https://www.cve.org/CVERecord?id=CVE-2017-16667)) + +## [1.1.22] (2017-10-28) + +### Fixed + +- stat free space for snapshot folder instead of backintime folder ([#733](https://github.com/bit-team/backintime/issues/733)) +- backintime root crontab doesn't run; missing line-feed 0x0A on last line ([#781](https://github.com/bit-team/backintime/issues/781)) +- can't open files with spaces in name ([#552](https://github.com/bit-team/backintime/issues/552)) + +## [1.1.20] (2017-04-09) + +### Fixed + +- polkit CheckAuthorization: race condition in privilege authorization ([CVE-2017-7572](https://www.cve.org/CVERecord?id=CVE-2017-7572)) + +## [1.1.18] (2017-03-29) + +### Fixed + +- manual snapshots from GUI didn't work ([#728](https://github.com/bit-team/backintime/issues/728)) + +## [1.1.16] (2017-03-28) + +### Fixed + +- start a new ssh-agent instance only if necessary ([#722](https://github.com/bit-team/backintime/issues/722)) +- OSError when running backup-job from systemd ([#720](https://github.com/bit-team/backintime/issues/720)) + +## [1.1.14] (2017-03-05) + +### Fixed + +- udev schedule not working ([#605](https://github.com/bit-team/backintime/issues/605)) +- Keyring doesn't work with KDE Plasma5 ([#545](https://github.com/bit-team/backintime/issues/545)) +- nameError in tools.make_dirs ([#622](https://github.com/bit-team/backintime/issues/622)) +- use current folder if no file is selected in files view +- restore filesystem-root without 'Full rsync mode' with ACL and/or xargs activated broke whole system ([#708](https://github.com/bit-team/backintime/issues/708)) + +## [1.1.12] (2016-01-11) + +### Fixed + +- remove x-terminal-emulator dependency ([#515](https://github.com/bit-team/backintime/issues/515)) +- AttributeError in About Dialog ([#515](https://github.com/bit-team/backintime/issues/515)) + +## [1.1.10] (2016-01-09) + +### Fixed + +- failed to remove empty lock file ([#505](https://github.com/bit-team/backintime/issues/505)) +- Restore the correct file owner and group fail if they are not present in system ([#58](https://github.com/bit-team/backintime/issues/58)) +- QObject::startTimer error on closing app +- FileNotFoundError while starting pw-cache from source +- suppress warning about failed inhibit suspend if run as root ([#500](https://github.com/bit-team/backintime/issues/500)) +- UI blocked/grayed out while removing snapshot ([#487](https://github.com/bit-team/backintime/issues/487)) +- pw-cache failed on leftover PID file, using ApplicationInstance now ([#468](https://github.com/bit-team/backintime/issues/468)) +- failed to parse some arguments ([#492](https://github.com/bit-team/backintime/issues/492)) +- failed to start GUI if launched from systray icon +- deleted snapshot is still listed in Timeline if using mode SSH ([#493](https://github.com/bit-team/backintime/issues/493)) +- PermissionError while deleting readonly files on sshfs mounted share ([#490](https://github.com/bit-team/backintime/issues/490)) +- create new encrypted profiles with encfs >= 1.8.0 failed ([#477](https://github.com/bit-team/backintime/issues/477)) +- AttributeError in common/tools.py if keyring is missing ([#473](https://github.com/bit-team/backintime/issues/473)) +- remote rename of 'new_snapshot' folder sometimes isn't recognized locally; rename local now (https://answers.launchpad.net/questions/271792) + +### Uncategorized + +- Add Icon 'show-hidden' ([#507](https://github.com/bit-team/backintime/issues/507)) +- subclass ApplicationInstance in GUIApplicationInstance to reduce redundant code +- speed up app start by adding snapshots to timeline in background thread +- add warning on failed permission restore ([#58](https://github.com/bit-team/backintime/issues/58)) +- continue an unfinished new_snapshot if possible ([#400](https://github.com/bit-team/backintime/issues/400)) +- Add Nautilus-like shortcuts for navigating in file browser ([#483](https://github.com/bit-team/backintime/issues/483)) +- speed up mounting of SSH+encrypted profiles +- Move source code and bug tracking to GitHub + +### Added + +- Modify for Full System Backup button to settings page, to change some profile settings +- get|set_list_value to configfile +- unittest (thanks to Dorian, Alexandre, Aurélien and Gregory from IAGL) + +## [1.1.8] (2015-09-28) + +### Fixed + +- unlock private SSH key run into 5sec timeout if password is empty +- BiT freeze when activate 'Decode path' in 'Snapshot Log View' +- empty gray window appears when starting the gui as root ([Launchpad#1493020](https://bugs.launchpad.net/backintime/+bug/1493020)) +- gnu_find_suffix_support doesn't set back to True ([Launchpad#1487781](https://bugs.launchpad.net/backintime/+bug/1487781)) +- lintian warning dbus-policy-without-send-destination +- dbus exception if dbus systembus is not running +- depend on virtual package cron-daemon instead of cron for compatibility with other cron implementations ([Debian#776856](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=776856)) +- wasn't able to start from alternate install dir ([Launchpad#478689](https://bugs.launchpad.net/backintime/+bug/478689)) +- wasn't able to start from source dir +- 'Inhibit Suspend' fails with 'org.freedesktop.PowerManagement.Inhibit' ([Launchpad#1485242](https://bugs.launchpad.net/backintime/+bug/1485242)) +- No mounting while selecting a secondary profile in the gui ([Launchpad#1481267](https://bugs.launchpad.net/backintime/+bug/1481267)) +- fix for bug [Launchpad#1419466](https://bugs.launchpad.net/backintime/+bug/1419466) broke crontab on Slackware +- fix for bug [Launchpad#1431305](https://bugs.launchpad.net/backintime/+bug/1431305) broke pw-cache on Ubuntu +- bash-complete +- Settings accepted empty strings for Host/User/Profile-ID ([Launchpad#1477733](https://bugs.launchpad.net/backintime/+bug/1477733)) +- IndexError on 'check_remote_commands' due to too long args ([Launchpad#1471930](https://bugs.launchpad.net/backintime/+bug/1471930)) +- Makefile has no uninstall target ([Launchpad#1469152](https://bugs.launchpad.net/backintime/+bug/1469152)) + +### Uncategorized + +- show current app name and profile ID in syslog ([Launchpad#906213](https://bugs.launchpad.net/backintime/+bug/906213)) +- Show 'Profiles' dropdown only in 'Last Log Viewer', add 'Snapshots' dropdown in 'Snapshot Log Viewer' ([Launchpad#1478219](https://bugs.launchpad.net/backintime/+bug/1478219)) +- do not restore permission if they are identical with current permissions +- security issue: do not run user-callback in a shell +- apply timestamps-in-gzip.patch from Debian backintime/1.1.6-1 package +- run multiple smart-remove jobs in one screen session ([Launchpad#1487781](https://bugs.launchpad.net/backintime/+bug/1487781)) +- use native Python code to check mountpoint +- Add expert option for stdout and stderr redirection in cronjobs (https://answers.launchpad.net/questions/270105) +- show 'man backintime' on Help; remove link to backintime.le-web.org ([Launchpad#1475995](https://bugs.launchpad.net/backintime/+bug/1475995)) +- add --local-backup, --no-local-backup and --delete option to restore on command-line ([Launchpad#1467239](https://bugs.launchpad.net/backintime/+bug/1467239)) +- rewrite command-line argument parsing. Now using argparse + +### Added + +- option to not log user-callback output +- error messages if PID file creation fail +- Warning about unsupported filesystems +- --debug argument +- 'backup on restore' option to confirm dialog +- check-config command for command-line +- expert option SSH command prefix + +### Removed + +- shebang in common/askpass.py and common/create-manpage-backintime-config.py + +## [1.1.6] (2015-06-27) + +### Uncategorized + +- show Profile name in systrayicon menu +- make own Exceptions a childclass from BackInTimeException +- Specifying the SSH private key whenever ssh is called ([Launchpad#1433682](https://bugs.launchpad.net/backintime/+bug/1433682)) +- add to in-/exclude directly from mainwindow ([Launchpad#1454856](https://bugs.launchpad.net/backintime/+bug/1454856)) +- add option to run Smart Remove in background on remote host ([Launchpad#1457210](https://bugs.launchpad.net/backintime/+bug/1457210)) +- Use current profile when starting GUI from Systray + +### Fixed + +- encrypted remote backup hangs on 'start encfsctl encode process' ([Launchpad#1455925](https://bugs.launchpad.net/backintime/+bug/1455925)) +- missing profile.name crashed GUI +- Segmentation fault caused by two QApplication instances ([Launchpad#1463732](https://bugs.launchpad.net/backintime/+bug/1463732)) +- no Changes [C] log entries with 'Check for changes' disabled ([Launchpad#1463367](https://bugs.launchpad.net/backintime/+bug/1463367)) +- some changed options from Settingsdialog where not respected during automatic tests after hitting OK +- python version check fails on python 3.3 ([Launchpad#1463686](https://bugs.launchpad.net/backintime/+bug/1463686)) +- pw-cache didn't start on Mint KDE because of missing stdout and stderr ([Launchpad#1431305](https://bugs.launchpad.net/backintime/+bug/1431305)) +- failed to restore file names with white spaces using CLI ([Launchpad#1435602](https://bugs.launchpad.net/backintime/+bug/1435602)) +- UnboundLocalError with 'last_snapshot' in _free_space ([Launchpad#1437623](https://bugs.launchpad.net/backintime/+bug/1437623)) + +### Removed + +- consolekit from dependencies + +## [1.1.4] (2015-03-22) + +### Uncategorized + +- add option to keep new snapshot with 'full rsync mode' regardless of changes ([Launchpad#1434722](https://bugs.launchpad.net/backintime/+bug/1434722)) +- remove base64 encoding for passwords as it doesn't add any security but broke the password process ([Launchpad#1431305](https://bugs.launchpad.net/backintime/+bug/1431305)) +- add confirm dialog before restoring ([Launchpad#438079](https://bugs.launchpad.net/backintime/+bug/438079)) +- cache uuid in config so it doesn't fail if the device isn't plugged in ([Launchpad#1426881](https://bugs.launchpad.net/backintime/+bug/1426881)) +- prevent snapshots from being removed with restore and delete; show warning if restore and delete filesystem root (https://answers.launchpad.net/questions/262837) +- use 'crontab' instead of 'crontab -' to read from stdin ([Launchpad#1419466](https://bugs.launchpad.net/backintime/+bug/1419466)) + +### Fixed + +- wrong quote in 'Save config file' +- Deleting the last snapshot does not update the last_snapshot symlink ([Launchpad#1434724](https://bugs.launchpad.net/backintime/+bug/1434724)) +- Wrong status text in the tray icon ([Launchpad#1429400](https://bugs.launchpad.net/backintime/+bug/1429400)) +- restore permissions of lots of files made BackInTime unresponsive ([Launchpad#1428423](https://bugs.launchpad.net/backintime/+bug/1428423)) +- failed to restore file owner and group +- OSError in free_space; add alternate method to get free space +- ugly theme while running as root on Gnome based DEs ([Launchpad#1418447](https://bugs.launchpad.net/backintime/+bug/1418447)) +- UnicodeError thrown if filename has broken charset ([Launchpad#1419694](https://bugs.launchpad.net/backintime/+bug/1419694)) + +### Added + +- option to run only one snapshot at a time +- warning about wrong Python version in configure +- bash-completion + +## [1.1.2] (2015-02-04) + +### Uncategorized + +- sort 'Backup folders' in main window +- save in- and exclude sort order +- use PolicyKit to install Udev rules +- move compression from install to build in Makefiles +- use pkexec to start backintime-qt4 as root + +## [1.1.0] (2015-01-15) + +### Added + +- tooltips for rsync options +- more user-callback events (on App start and exit, on mount and unmount) +- context menu to files view +- more default exclude; remove [Cc]ache* from exclude +- option for custom rsync-options +- ProgressBar for rsync +- progress for smart-remove +- --gksu/--gksudo arg to qt4/configure + +### Uncategorized + +- make only one debian/control +- multiselect files to restore ([Launchpad#1135886](https://bugs.launchpad.net/backintime/+bug/1135886)) +- force run manual snapshots on battery ([Launchpad#861553](https://bugs.launchpad.net/backintime/+bug/861553)) +- backup encfs config to local config folder +- apply 'install-docs-move.patch' from Debian package by Jonathan Wiltshire +- add restore option to delete new files during restore ([Launchpad#1371951](https://bugs.launchpad.net/backintime/+bug/1371951)) +- use flock to prevent two instances running at the same time +- restore config dialog added ([Launchpad#480391](https://bugs.launchpad.net/backintime/+bug/480391)) +- inhibit suspend/hibernate while take_snapshot or restore +- use more reliable code for get_user +- implement anacrons functions inside BIT => more flexible schedules and no new timestamp if there was an error +- automatically run in background if started with 'backintime --backup-job' +- fix typos and style warnings in manpages reported by Lintian (https://lintian.debian.org/full/jmw@debian.org.html#backintime_1.0.34-0.1) +- add exclude files by size ([Launchpad#823719](https://bugs.launchpad.net/backintime/+bug/823719)) +- optional run 'rsync' with 'nocache' ([Launchpad#1344528](https://bugs.launchpad.net/backintime/+bug/1344528)) +- mark invalid exclude pattern with mode ssh-encrypted +- make Settingsdialog tabs scrollable +- remove colon (: ) restriction in exclude pattern +- prevent starting new snapshot if restore is running +- add top-level directory for tarball ([Launchpad#1359076](https://bugs.launchpad.net/backintime/+bug/1359076)) +- multi selection in timeline => remove multiple snapshots with one click +- print warning if started with sudo +- ask to include symlinks target instead link ([Launchpad#1117709](https://bugs.launchpad.net/backintime/+bug/1117709)) +- port to Python 3.x +- returncode >0 if there was an error ([Launchpad#1040995](https://bugs.launchpad.net/backintime/+bug/1040995)) +- Enable user-callback script to cancel a backup by returning a non-zero exit code. +- merge backintime-notify into backintime-qt4 +- remember last path for each profile ([Launchpad#1254870](https://bugs.launchpad.net/backintime/+bug/1254870)) +- sort include and exclude list ([Launchpad#1193149](https://bugs.launchpad.net/backintime/+bug/1193149)) +- Timeline show tooltip 'Last check' +- show hidden files in FileDialog ([Launchpad#995925](https://bugs.launchpad.net/backintime/+bug/995925)) +- add button text for all buttons ([Launchpad#992020](https://bugs.launchpad.net/backintime/+bug/992020)) +- add shortcuts ([Launchpad#686694](https://bugs.launchpad.net/backintime/+bug/686694)) +- add menubar ([Launchpad#528851](https://bugs.launchpad.net/backintime/+bug/528851)) +- port KDE4 GUI to pure Qt4 to replace both KDE4 and Gnome GUI + +### Removed + +- 'Auto Host/User/Profile-ID' as this is more confusing than helping +- snapshots from commandline +- old status-bar message after a snapshot crashed. + +### Fixed + +- check procname of pid-locks ([Launchpad#1341414](https://bugs.launchpad.net/backintime/+bug/1341414)) +- Port check failed on IPv6 ([Launchpad#1361634](https://bugs.launchpad.net/backintime/+bug/1361634)) +- 'inotify_add_watch failed' while closing BIT +- systray icon didn't show up ([Launchpad#658424](https://bugs.launchpad.net/backintime/+bug/658424)) + +## [1.0.40] (2014-11-02) + +### Uncategorized + +- use fingerprint to check if ssh key was unlocked correctly (https://answers.launchpad.net/questions/256408) +- add fallback method to get UUID (https://answers.launchpad.net/questions/254140) + +### Fixed + +- 'Attempt to unlock mutex that was not locked'... this time for good + +## [1.0.38] (2014-10-01) + +### Fixed + +- 'Attempt to unlock mutex that was not locked' in gnomeplugin (https://answers.launchpad.net/questions/255225) +- housekeeping by gnome-session-daemon might delete backup and original data ([Launchpad#1374343](https://bugs.launchpad.net/backintime/+bug/1374343)) +- Type Error in 'backintime --decode' ([Launchpad#1365072](https://bugs.launchpad.net/backintime/+bug/1365072)) +- take_snapshot didn't wait for snapshot folder come available if notifications are disabled ([Launchpad#1332979](https://bugs.launchpad.net/backintime/+bug/1332979)) + +### Uncategorized + +- compare os.path.realpath instead of os.stat to get devices UUID + +## [1.0.36] (2014-08-06) + +### Uncategorized + +- remove UbuntuOne from exclude ([Launchpad#1340131](https://bugs.launchpad.net/backintime/+bug/1340131)) +- Gray out 'Add Profile' if 'Main Profile' isn't configured yet ([Launchpad#1335545](https://bugs.launchpad.net/backintime/+bug/1335545)) +- Don't check for fuse group-membership if group doesn't exist +- disable keyring for root + +### Fixed + +- backintime-kde4 as root failed to load ssh-key ([Launchpad#1276348](https://bugs.launchpad.net/backintime/+bug/1276348)) +- kdesystrayicon.py crashes because of missing environ ([Launchpad#1332126](https://bugs.launchpad.net/backintime/+bug/1332126)) +- OSError if sshfs/encfs is not installed ([Launchpad#1316288](https://bugs.launchpad.net/backintime/+bug/1316288)) +- TypeError in config.py check_config() (https://bugzilla.redhat.com/show_bug.cgi?id=1091644) +- unhandled exception in create_last_snapshot_symlink() ([Launchpad#1269991](https://bugs.launchpad.net/backintime/+bug/1269991)) + +## [1.0.34] (2013-12-21) + +### Uncategorized + +- sync/flush all disks before shutdown ([Launchpad#1261031](https://bugs.launchpad.net/backintime/+bug/1261031)) + +### Fixed + +- BIT running as root shutdown after snapshot, regardless of option checked ([Launchpad#1261022](https://bugs.launchpad.net/backintime/+bug/1261022)) + +## [1.0.32] (2013-12-13) + +### Fixed + +- cron scheduled snapshots won't start with 1.0.30 + +## [1.0.30] (2013-12-12) + +### Uncategorized + +- scheduled and manual snapshots use --config +- make configure scripts portable ([Launchpad#377429](https://bugs.launchpad.net/backintime/+bug/377429)) +- add symlink last_snapshot ([Launchpad#787118](https://bugs.launchpad.net/backintime/+bug/787118)) +- add option to run rsync with 'nice' or 'ionice' on remote host ([Launchpad#1240301](https://bugs.launchpad.net/backintime/+bug/1240301)) +- add Shutdown button to shutdown system after snapshot has finished ([Launchpad#838742](https://bugs.launchpad.net/backintime/+bug/838742)) +- wrap long lines for syslog + +### Fixed + +- udev rule doesn't finish ([Launchpad#1249466](https://bugs.launchpad.net/backintime/+bug/1249466)) +- multiple errors in PPA build process; reorganize updateversion.sh +- Mate and xfce desktop didn't show systray icon ([Launchpad#658424](https://bugs.launchpad.net/backintime/+bug/658424)/comments/31) +- Ubuntu Lucid doesn't provide SecretServiceKeyring ([Launchpad#1243911](https://bugs.launchpad.net/backintime/+bug/1243911)) +- 'gksu backintime-gnome' failed with dbus.exceptions.DBusException + +### Added + +- virtual package backintime-kde for PPA + +## [1.0.28] (2013-10-19) + +### Removed + +- config on 'apt-get purge' + +### Added + +- more options for configure scripts; update README +- udev schedule (run BIT as soon as the drive is connected) + +### Fixed + +- AttributeError with python-keyring>1.6.1 ([Launchpad#1234024](https://bugs.launchpad.net/backintime/+bug/1234024)) +- TypeError: KDirModel.removeColumns() is a private method in kde4/app.py ([Launchpad#1232694](https://bugs.launchpad.net/backintime/+bug/1232694)) +- sshfs mount disconnect after a while due to some firewalls (add ServerAliveInterval) (https://answers.launchpad.net/backintime/+question/235685) +- Ping fails if ICMP is disabled on remote host ([Launchpad#1226718](https://bugs.launchpad.net/backintime/+bug/1226718)) +- KeyError in getgrnam if there is no 'fuse' group ([Launchpad#1225561](https://bugs.launchpad.net/backintime/+bug/1225561)) +- anacrontab won't work with profilename with spaces ([Launchpad#1224620](https://bugs.launchpad.net/backintime/+bug/1224620)) +- NameError in tools.move_snapshots_folder ([Launchpad#871466](https://bugs.launchpad.net/backintime/+bug/871466)) +- KPassivePopup is not defined ([Launchpad#871475](https://bugs.launchpad.net/backintime/+bug/871475)) +- ValueError while reading pw-cache PID (https://answers.launchpad.net/backintime/+question/235407) + +### Uncategorized + +- add '--checksum' commandline option ([Launchpad#886021](https://bugs.launchpad.net/backintime/+bug/886021)) +- multi selection for include and exclude list ([Launchpad#660753](https://bugs.launchpad.net/backintime/+bug/660753)) + +## [1.0.26] (2013-09-07) + +### Uncategorized + +- add feature: keep min free inodes +- roll back commit 836.1.5 (check free-space on ssh remote host): statvfs DOES work over sshfs. But not with quite outdated sshd +- add feature: restore from command line; add option --config +- use 'ps ax' to check if 'backintime --pw-cache' is still running +- mount after locking, unmount before unlocking in take_snapshot +- redirect logger.error and .warning to stderr; new argument --quiet +- deactivate 'Save Password' if no keyring is available +- use Password-cache for user-input too +- handle two Passwords +- add 'SSH encrypted': mount / with encfs reverse and sync encrypted with rsync. EXPERIMENTAL! +- add 'Local encrypted': mount encfs + +### Added + +- daily anacron schedule +- delete button and 'list only equal' in Snapshot dialog; multiSelect in snapshot list +- manpage backintime-config and config-examples +- option --bwlimit for rsync + +### Fixed + +- Restore makes files public during the operation +- Cannot keep modifications to cron ([Launchpad#698106](https://bugs.launchpad.net/backintime/+bug/698106)) +- cannot stat 'backintime-kde4-root.desktop.kdesudo' ([Launchpad#696659](https://bugs.launchpad.net/backintime/+bug/696659)) +- unreadable dark KDE color schemes ([Launchpad#1184920](https://bugs.launchpad.net/backintime/+bug/1184920)) +- permission denied if remote uid wasn't the same as local uid + +## [1.0.24] (2013-05-08) + +### Uncategorized + +- hide check_for_canges if full_rsync_mode is checked +- DEFAULT_EXCLUDE system folders with /foo/* so at least the folder itself will backup +- DEFAULT_EXCLUDE /run; exclude MOUNT_ROOT with higher priority and not with DEFAULT_EXCLUDE anymore +- 'Save Password' default off to avoid problems with existing profiles +- if restore uid/gid failed try to restore at least gid +- SSH need to store permissions in separate file with "Full rsync mode" because remote user might not be able to store ownership +- switch to 'find -exec cmd {} +' ([Launchpad#1157639](https://bugs.launchpad.net/backintime/+bug/1157639)) + +### Fixed + +- 'CalledProcessError' object has no attribute 'strerror' +- quote rsync remote path with spaces +- restore permission failed on "Full rsync mode" +- glib.GError: Unknown internal child: selection +- GtkWarning: Unknown property: GtkLabel.margin-top +- check keyring backend only if password is needed + +### Changed + +- all indent tabs to 4 spaces + +## [1.0.22] (2013-03-26) + +### Uncategorized + +- check free-space on ssh remote host (statvfs didn't work over sshfs) + +### Added + +- Password storage mode ssh +- "Full rsync mode" (can be faster but ...) + +### Fixed + +- "Restore to..." failed due to spaces in directory name ([Launchpad#1096319](https://bugs.launchpad.net/backintime/+bug/1096319)) +- host not found in known_hosts if port != 22 ([Launchpad#1130356](https://bugs.launchpad.net/backintime/+bug/1130356)) +- sshtools.py used not POSIX conform conditionals + +## [1.0.20] (2012-12-15) + +### Fixed + +- restore remote path with spaces using mode ssh returned error + +## [1.0.18] (2012-11-17) + +### Uncategorized + +- Fix packages: man & translations +- Map multiple arguments for gettext so they can be rearranged by translators + +### Fixed + +- [Launchpad#1077446](https://bugs.launchpad.net/backintime/+bug/1077446) +- [Launchpad#1078979](https://bugs.launchpad.net/backintime/+bug/1078979) +- [Launchpad#1079479](https://bugs.launchpad.net/backintime/+bug/1079479) + +## [1.0.16] (2012-11-15) + +### Uncategorized + +- Fix a package dependency problem ... this time for good ([Launchpad#1077446](https://bugs.launchpad.net/backintime/+bug/1077446)) + +## [1.0.14] (2012-11-09) + +### Fixed + +- a package dependency problem + +## [1.0.12] (2012-11-08) + +### Uncategorized + +- Add links to: website, documentation, report a bug, answers, faq +- Use libnotify for gnome/kde4 notifications instead of gnome specific libraries +- Add more schedule options: every 30 min, every 2 hours, every 4 hours, every 6 hours & every 12 hours + +### Fixed + +- [Launchpad#1059247](https://bugs.launchpad.net/backintime/+bug/1059247) +- wrong path if restore system root +- glade (xml) files did not translate +- [Launchpad#1073867](https://bugs.launchpad.net/backintime/+bug/1073867) + +### Added + +- generic mount-framework +- mode 'SSH' for backups on remote host using ssh protocol. + +## [1.0.10] (2012-03-06) + +### Added + +- "Restore to ..." in replacement of copy (with or without drag & drop) because copy don't restore user/group/rights + +## [1.0.8] (2011-06-18) + +### Fixed + +- [Launchpad#723545](https://bugs.launchpad.net/backintime/+bug/723545) +- [Launchpad#705237](https://bugs.launchpad.net/backintime/+bug/705237) +- [Launchpad#696663](https://bugs.launchpad.net/backintime/+bug/696663) +- [Launchpad#671946](https://bugs.launchpad.net/backintime/+bug/671946) + +## [1.0.6] (2011-01-02) + +### Fixed + +- [Launchpad#676223](https://bugs.launchpad.net/backintime/+bug/676223) +- [Launchpad#672705](https://bugs.launchpad.net/backintime/+bug/672705) + +### Uncategorized + +- Smart remove: configurable options ([Launchpad#406765](https://bugs.launchpad.net/backintime/+bug/406765)) + +## [1.0.4] (2010-10-28) + +### Uncategorized + +- SettingsDialog: show highly recommended excludes +- Option to use checksum to detect changes ([Launchpad#666964](https://bugs.launchpad.net/backintime/+bug/666964)) +- Option to select log verbosity ([Launchpad#664423](https://bugs.launchpad.net/backintime/+bug/664423)) +- Gnome: use gloobus-preview if installed + +### Fixed + +- [Launchpad#664783](https://bugs.launchpad.net/backintime/+bug/664783) + +## [1.0.2] (2010-10-16) + +### Uncategorized + +- reduce log file (no more duplicate "Compare with..." lines) +- declare backintime-kde4 packages as a replacement of backintime-kde + +## [1.0] (2010-10-16) + +### Uncategorized + +- add '.dropbox*' to default exclude patterns ([Launchpad#628172](https://bugs.launchpad.net/backintime/+bug/628172)) +- add option to take a snapshot at every boot ([Launchpad#621810](https://bugs.launchpad.net/backintime/+bug/621810)) +- add continue on errors ([Launchpad#616299](https://bugs.launchpad.net/backintime/+bug/616299)) +- add expert options: copy unsafe links & copy links +- "user-callback" replace "user.callback" and receive profile information +- documentation: on-line only (easier to maintain) +- merge with: lp:~dave2010/backintime/minor-edits +- merge with: lp:~mcfonty/backintime/unique-snapshots-view +- reduce memory usage during compare with previous snapshot process +- custom backup hour (for daily backups or mode): [Launchpad#507451](https://bugs.launchpad.net/backintime/+bug/507451) +- smart remove was slightly changed ([Launchpad#502435](https://bugs.launchpad.net/backintime/+bug/502435)) +- make backup on restore optional +- fix bug that could cause "ghost" folders in snapshots (LP: 406092) +- fix bug that converted / into // ([Launchpad#455149](https://bugs.launchpad.net/backintime/+bug/455149)) +- remove "schedule per included directory" (profiles do that) (+ bug [Launchpad#412470](https://bugs.launchpad.net/backintime/+bug/412470)) +- fig bug: [Launchpad#489380](https://bugs.launchpad.net/backintime/+bug/489380) +- update Slovak translation (Tomáš Vadina ) +- multiple profiles support +- GNOME: fix notification +- backintime snapshot folder is restructured to ../backintime/machine/user/profile_id/ +- added a desktop file for kdesu and a test if kdesu or kdesudo should be used ([Launchpad#389988](https://bugs.launchpad.net/backintime/+bug/389988)) +- added expert option to disable snapshots when on battery ([Launchpad#388178](https://bugs.launchpad.net/backintime/+bug/388178)) +- fix bug handling big files by the GNOME GUI ([Launchpad#409130](https://bugs.launchpad.net/backintime/+bug/409130)) +- fix bug in handling of & characters by GNOME GUI ([Launchpad#415848](https://bugs.launchpad.net/backintime/+bug/415848)) +- fix a security bug in chmods before snapshot removal ([Launchpad#419774](https://bugs.launchpad.net/backintime/+bug/419774)) +- snapshots are stored entirely read-only ([Launchpad#386275](https://bugs.launchpad.net/backintime/+bug/386275)) +- fix exclude patterns in KDE4 ([Launchpad#432537](https://bugs.launchpad.net/backintime/+bug/432537)) +- fix opening german files with external applications in KDE ([Launchpad#404652](https://bugs.launchpad.net/backintime/+bug/404652)) +- changed default exclude patterns to caches, thumbnails, trashbins, and backups ([Launchpad#422132](https://bugs.launchpad.net/backintime/+bug/422132)) +- write access to snapshot folder is checked & change to snapshot version 2 ([Launchpad#423086](https://bugs.launchpad.net/backintime/+bug/423086)) +- fix small bugs (a.o. [Launchpad#474307](https://bugs.launchpad.net/backintime/+bug/474307)) +- Used a more standard crontab syntax ([Launchpad#409783](https://bugs.launchpad.net/backintime/+bug/409783)) +- Stop the "Over zealous removal of crontab entries" ([Launchpad#451811](https://bugs.launchpad.net/backintime/+bug/451811)) + +### Fixed + +- xattr +- [Launchpad#588841](https://bugs.launchpad.net/backintime/+bug/588841) +- [Launchpad#588215](https://bugs.launchpad.net/backintime/+bug/588215) +- [Launchpad#588393](https://bugs.launchpad.net/backintime/+bug/588393) +- [Launchpad#426400](https://bugs.launchpad.net/backintime/+bug/426400) +- [Launchpad#575022](https://bugs.launchpad.net/backintime/+bug/575022) +- [Launchpad#571894](https://bugs.launchpad.net/backintime/+bug/571894) +- [Launchpad#553441](https://bugs.launchpad.net/backintime/+bug/553441) +- [Launchpad#550765](https://bugs.launchpad.net/backintime/+bug/550765) +- [Launchpad#507246](https://bugs.launchpad.net/backintime/+bug/507246) +- [Launchpad#538855](https://bugs.launchpad.net/backintime/+bug/538855) +- [Launchpad#386230](https://bugs.launchpad.net/backintime/+bug/386230) +- [Launchpad#527039](https://bugs.launchpad.net/backintime/+bug/527039) +- [Launchpad#520956](https://bugs.launchpad.net/backintime/+bug/520956) +- [Launchpad#520930](https://bugs.launchpad.net/backintime/+bug/520930) +- [Launchpad#521223](https://bugs.launchpad.net/backintime/+bug/521223) +- [Launchpad#516066](https://bugs.launchpad.net/backintime/+bug/516066) +- [Launchpad#512813](https://bugs.launchpad.net/backintime/+bug/512813) +- [Launchpad#503859](https://bugs.launchpad.net/backintime/+bug/503859) +- [Launchpad#501285](https://bugs.launchpad.net/backintime/+bug/501285) +- [Launchpad#493558](https://bugs.launchpad.net/backintime/+bug/493558) +- [Launchpad#441628](https://bugs.launchpad.net/backintime/+bug/441628) +- [Launchpad#489319](https://bugs.launchpad.net/backintime/+bug/489319) +- [Launchpad#447841](https://bugs.launchpad.net/backintime/+bug/447841) +- [Launchpad#412695](https://bugs.launchpad.net/backintime/+bug/412695) + +### Added + +- error log and error log view dialog (Gnome & KDE4) +- ionice support for user/cron backup process +- the possibility to include other snapshot folders within a profile, it can only read those, there is not a GUI implementation yet +- a tag suffix to the snapshot_id, to avoid double snapshot_ids + +## [0.9.26] (2009-05-19) + +### Uncategorized + +- update translations from Launchpad +- Fix a bug in smart-remove algorithm ([Launchpad#376104](https://bugs.launchpad.net/backintime/+bug/376104)) +- update German translation (Michael Wiedmann ) +- use only 'folder' term (more consistent with GNOME/KDE) +- add 'expert option': enable/disable nice for cron jobs +- GNOME & KDE4: refresh snapshots button force files view to update too +- you can include a backup parent directory (backup directory will auto-exclude itself) + +### Fixed + +- [Launchpad#374477](https://bugs.launchpad.net/backintime/+bug/374477) +- [Launchpad#375113](https://bugs.launchpad.net/backintime/+bug/375113) +- some small bugs + +### Added + +- '--no-check' option to configure scripts + +## [0.9.24] (2009-05-07) + +### Uncategorized + +- update translations +- KDE4: fix python string <=> QString problems +- KDE4 FilesView/SnapshotsDialog: ctrl-click just select (don't execute) +- KDE4: fix crush after "take snapshot" process ([Launchpad#366241](https://bugs.launchpad.net/backintime/+bug/366241)) +- store basic permission in a special file so it can restore them correctly (event from NTFS) +- implement Gnome/KDE4 systray icons and user.callback as plugins +- reorganize code: common/GNOME/KDE4 +- GNOME: break the big glade file in multiple file +- backintime is no longer aware of 'backintime-gnome' and 'backintime-kde4' + +### Added + +- config version + +## [0.9.22.1] (2009-04-27) + +### Fixed + +- French translation + +## [0.9.22] (2009-04-24) + +### Uncategorized + +- update translations from Launchpad +- KDE4: fix some translation problems +- update German translation (Michael Wiedmann ) +- create directory now use python os.makedirs (replace use of mkdir command) +- KDE4: fix a crush related to QString - python string conversion +- GNOME & KDE4 SettingsDialog: if schedule automatic backups per directory is set, global schedule is hidden +- GNOME FilesView: thread "*~" files (backup files) as hidden files +- GNOME: use gtk-preferences icon for SettingsDialog (replace gtk-execute icon) +- expert option: $XDG_CONFIG_HOME/backintime/user.callback (if exists) is called a different steps +- add more command line options: --snapshots-list, --snapshots-list-path, --last-snapshot, --last-snapshot-path +- follow FreeDesktop directories specs: +- new install system: use more common steps (./configure; make; sudo make install) + +### Removed + +- --safe-links for save/restore (this means copy symlinks as symlinks) + +## [0.9.20] (2009-04-06) + +### Uncategorized + +- smart remove: fix an important bug and make it more verbose in syslog +- update Spanish translation (Francisco Manuel García Claramonte ) + +## [0.9.18] (2009-04-02) + +### Uncategorized + +- update translations from Launchpad +- update Slovak translation (Tomáš Vadina ) +- update French translation (Michel Corps ) +- update German translation (Michael Wiedmann ) +- GNOME bugfix: fix a crush in files view for files with special characters (ex: "a%20b") +- GNOME SettingsDialog bugfix: if snapshots path is a new created folder, snapshots navigation (files view) don't work +- update doc +- GNOME & KDE4 MainWindow: Rename "Places" list with "Snapshots" +- GNOME SettingsDialog bugfix: modify something, then press cancel. If you reopen the dialog it show wrong values (the ones before cancel) +- GNOME & KDE4: add root mode menu entries (use gksu for gnome and kdesudo for kde) +- GNOME & KDE4: MainWindow - Files view: if the current directory don't exists in current snapshot display a message +- SettingDialog: add an expert option to enable to schedule automatic backups per directory +- SettingDialog: schedule automatic backups - if the application can't find crontab it show an error +- SettingDialog: if the application can't write in snapshots directory there should be an error message +- GNOME & KDE4: rework settings dialog +- SettingDialog: add an option to enable/disable notifications + +### Added + +- Polish translation (Paweł Hołuj ) +- cron in common package dependencies + +## [0.9.16.1] (2009-03-16) + +### Fixed + +- a bug/crush for French version + +## [0.9.16] (2009-03-13) + +### Uncategorized + +- update Spanish translation (Francisco Manuel García Claramonte ) +- update Swedish translation (Niklas Grahn ) +- update French translation (Michel Corps ) +- update German translation (Michael Wiedmann ) +- update Slovenian translation (Vanja Cvelbar ) +- don't show the snapshot that is being taken in snapshots list +- GNOME & KDE4: when the application starts and snapshots directory don't exists show a messagebox +- give more information for 'take snapshot' progress (to prove that is not blocked) +- MainWindow: rename 'Timeline' column with 'Snapshots' +- when it tries to take a snapshot if the snapshots directory don't exists +- GNOME & KDE4: add notify if the snapshots directory don't exists +- KDE4: rework MainWindow + +### Added + +- Slovak translation (Tomáš Vadina ) + +## [0.9.14] (2009-03-05) + +### Uncategorized + +- update German translation (Michael Wiedmann ) +- update Swedish translation (Niklas Grahn ) +- update Spanish translation (Francisco Manuel García Claramonte ) +- update French translation (Michel Corps ) +- GNOME & KDE4: rework MainWindow +- GNOME & KDE4: rework SettingsDialog +- GNOME & KDE4: add "smart" remove + +## [0.9.12] (2009-02-28) + +### Fixed + +- now if you include ".abc" folder and exclude ".*", ".abc" will be saved in the snapshot +- bookmarks with special characters + +### Uncategorized + +- KDE4: add help + +### Added + +- Slovenian translation (Vanja Cvelbar ) + +## [0.9.10] (2009-02-24) + +### Added + +- Swedish translation (Niklas Grahn ) + +### Uncategorized + +- KDE4: drop and drop from backintime files view to any file manager + +### Fixed + +- fix a segfault when running from cron + +## [0.9.8] (2009-02-20) + +### Uncategorized + +- update Spanish translation (Francisco Manuel García Claramonte ) +- unsafe links are ignored (that means that a link to a file/directory outside of include directories are ignored) +- KDE4: add copy to clipboard +- KDE4: sort files by name, size or date +- cron 5/10 minutes: replace multiple lines with a single crontab line using divide (*/5 or */10) +- cron: when called from cron redirect output (stdout & stderr) to /dev/null + +### Fixed + +- unable to restore files that contains space char in their name + +## [0.9.6] (2009-02-09) + +### Uncategorized + +- update Spanish translation (Francisco Manuel García Claramonte ) +- update German translation (Michael Wiedmann ) +- GNOME: update docbook +- KDE4: add snapshots dialog +- GNOME & KDE4: add update snapshots button +- GNOME: handle special folders icons (home, desktop) + +## [0.9.4] (2009-01-30) + +### Uncategorized + +- update German translation (Michael Wiedmann ) +- gnome: better handling of 'take snapshot' status icon +- KDE4 (>= 4.1): first version (not finished) +- update man + +## [0.9.2] (2009-01-16) + +### Uncategorized + +- update Spanish translation (Francisco Manuel García Claramonte ) +- update German translation (Michael Wiedmann ) +- replace diff with rsync to check if a new snapshot is needed +- code cleanup + +### Fixed + +- if you add "/a" in include directories and "/a/b" in exclude patterns, "/a/b*" items +- it does not include ".*" items even if they are not excluded + +### Added + +- show hidden & backup files toggle button for files view + +## [0.9] (2009-01-09) + +### Uncategorized + +- update Spanish translation (Francisco Manuel García Claramonte ) +- make deb packages more debian friendly (thanks to Michael Wiedmann ) +- update German translation (Michael Wiedmann ) +- better separation between common and gnome specific files and +- code cleanup + +### Fixed + +- when you open snapshots dialog for the second time ( or more ) and you make a diff + +## [0.8.20] (2008-12-22) + +### Fixed + +- sorting files/directories by name is now case insensitive + +### Uncategorized + +- getmessages.sh: ignore "gtk-" items (this are gtk stock item ids and should not be changed) + +## [0.8.18] (2008-12-17) + +### Uncategorized + +- update man/docbook + +### Added + +- sort columns in MainWindow/FileView (by name, by size or by date) and SnapshotsDialog (by date) + +### Fixed + +- German translation (Michael Wiedmann ) + +## [0.8.16] (2008-12-11) + +### Uncategorized + +- add Drag & Drop from MainWindow: FileView/SnapshotsDialog to Nautilus +- update German translation (Michael Wiedmann ) + +## [0.8.14] (2008-12-07) + +### Added + +- more command line parameters ( --version, --snapshots, --help ) + +### Fixed + +- a crush for getting info on dead symbolic links + +### Uncategorized + +- when taking a new backup based on the previous one don't copy the previous extra info (ex: name) +- copy unsafe links when taking a snapshot + +## [0.8.12] (2008-12-01) + +### Added + +- German translation (Michael Wiedmann ) +- SnapshotNameDialog +- Name/Remove snapshot in main toolbar + +### Changed + +- the way it detects if the mainwindow is the active window (no dialogs) + +### Uncategorized + +- toolbars: show icons only +- update Spanish translation (Francisco Manuel García Claramonte ) + +## [0.8.10] (2008-11-22) + +### Uncategorized + +- SnapshotsDialog: add right-click popup-menu and a toolbar with copy & restore buttons +- use a more robust backup lock file +- log using syslog +- update Spanish translation (Francisco Manuel García Claramonte ) + +### Fixed + +- a small bug in copy to clipboard + +## [0.8.8] (2008-11-19) + +### Uncategorized + +- SnapshotsDialog: add diff +- update Spanish translation (Francisco Manuel García Claramonte ) + +## [0.8.6] (2008-11-17) + +### Fixed + +- change backup path crush + +### Added + +- SnapshotsDialog + +## [0.8.2] (2008-11-14) + +### Uncategorized + +- add right-click menu in files list: open (using gnome-open), copy (you can paste in Nautilus), restore (for snapshots only) + +### Added + +- Copy toolbar button for files list + +## [0.8.1] (2008-11-10) + +### Added + +- every 5/10 minutes automatic backup + +## [0.8] (2008-11-07) + +### Uncategorized + +- don't show backup files (*~) +- makedeb.sh: make a single package with all languages included +- install.sh: install all languages +- the application can be started with a 'path' to a folder or file as command line parameter +- when the application start, if it is already running pass its command line to the first instance (this allow a basic integration with file-managers - see README) + +### Added + +- backup files to default exclude patterns (*~) +- English manual (man) +- English help (docbook) +- help button in main toolbar + +### Fixed + +- when the application was started a second time it raise the first application's window but not always focused + +## [0.7.4] (2008-11-03) + +### Uncategorized + +- if there is already a GUI instance running raise it + +### Added + +- Spanish translation (Francisco Manuel García Claramonte ) + +## [0.7.2] (2008-10-28) + +### Uncategorized + +- better integration with gnome icons (use mime-types) +- remember last path +- capitalize month in timeline (bug in french translation) + +## [0.7] (2008-10-22) + +### Fixed + +- cron segfault +- a crush when launched the very first time (not configured) + +### Uncategorized + +- multi-lingual support + +### Added + +- French translation + +## [0.6.4] (2008-10-20) + +### Removed + +- About & Settings dialogs from the pager + +### Uncategorized + +- allow only one instance of the application + +## [0.6.2] (2008-10-16) + +### Uncategorized + +- remember window position & size + +## [0.6] (2008-10-13) + +### Uncategorized + +- when it make a snapshot it display an icon in systray area +- the background color for group items in timeline and places reflect more +- during restore only restore button is grayed ( even if everything is blocked ) + +## [0.5.1] (2008-10-10) + +### Added + +- size & date columns in files view + +### Changed + +- some texts + +## [0.5] (2008-10-03) + +### Uncategorized + +- This is the first release. + +[2.0.0]: https://github.com/bit-team/backintime/releases/tag/v2.0.0 +[1.6.2]: https://github.com/bit-team/backintime/releases/tag/v1.6.2 +[1.6.1]: https://github.com/bit-team/backintime/releases/tag/v1.6.1 +[1.6.0]: https://github.com/bit-team/backintime/releases/tag/v1.6.0 +[1.5.6]: https://github.com/bit-team/backintime/releases/tag/v1.5.6 +[1.5.5]: https://github.com/bit-team/backintime/releases/tag/v1.5.5 +[1.5.4]: https://github.com/bit-team/backintime/releases/tag/v1.5.4 +[1.5.3]: https://github.com/bit-team/backintime/releases/tag/v1.5.3 +[1.5.2]: https://github.com/bit-team/backintime/releases/tag/v1.5.2 +[1.5.1]: https://github.com/bit-team/backintime/releases/tag/v1.5.1 +[1.5.0]: https://github.com/bit-team/backintime/releases/tag/v1.5.0 +[1.4.3]: https://github.com/bit-team/backintime/releases/tag/v1.4.3 +[1.4.1]: https://github.com/bit-team/backintime/releases/tag/v1.4.1 +[1.4.0]: https://github.com/bit-team/backintime/releases/tag/v1.4.0 +[1.3.3]: https://github.com/bit-team/backintime/releases/tag/v1.3.3 +[1.3.2]: https://github.com/bit-team/backintime/releases/tag/v1.3.2 +[1.3.1]: https://github.com/bit-team/backintime/releases/tag/v1.3.1 +[1.3.0]: https://github.com/bit-team/backintime/releases/tag/v1.3.0 +[1.2.1]: https://github.com/bit-team/backintime/releases/tag/v1.2.1 +[1.2.0]: https://github.com/bit-team/backintime/releases/tag/v1.2.0 +[1.1.24]: https://github.com/bit-team/backintime/releases/tag/v1.1.24 +[1.1.22]: https://github.com/bit-team/backintime/releases/tag/v1.1.22 +[1.1.20]: https://github.com/bit-team/backintime/releases/tag/v1.1.20 +[1.1.18]: https://github.com/bit-team/backintime/releases/tag/v1.1.18 +[1.1.16]: https://github.com/bit-team/backintime/releases/tag/v1.1.16 +[1.1.14]: https://github.com/bit-team/backintime/releases/tag/v1.1.14 +[1.1.12]: https://github.com/bit-team/backintime/releases/tag/v1.1.12 +[1.1.10]: https://github.com/bit-team/backintime/releases/tag/v1.1.10 +[1.1.8]: https://github.com/bit-team/backintime/releases/tag/v1.1.8 +[1.1.6]: https://github.com/bit-team/backintime/releases/tag/v1.1.6 +[1.1.4]: https://github.com/bit-team/backintime/releases/tag/v1.1.4 +[1.1.2]: https://github.com/bit-team/backintime/releases/tag/v1.1.2 +[1.1.0]: https://github.com/bit-team/backintime/releases/tag/v1.1.0 +[1.0.40]: https://github.com/bit-team/backintime/releases/tag/v1.0.40 +[1.0.38]: https://github.com/bit-team/backintime/releases/tag/v1.0.38 +[1.0.36]: https://github.com/bit-team/backintime/releases/tag/v1.0.36 +[1.0.34]: https://github.com/bit-team/backintime/releases/tag/v1.0.34 +[1.0.32]: https://github.com/bit-team/backintime/releases/tag/v1.0.32 +[1.0.30]: https://github.com/bit-team/backintime/releases/tag/v1.0.30 +[1.0.28]: https://github.com/bit-team/backintime/releases/tag/v1.0.28 +[1.0.26]: https://github.com/bit-team/backintime/releases/tag/v1.0.26 +[1.0.24]: https://github.com/bit-team/backintime/releases/tag/v1.0.24 +[1.0.22]: https://github.com/bit-team/backintime/releases/tag/v1.0.22 +[1.0.20]: https://github.com/bit-team/backintime/releases/tag/v1.0.20 +[1.0.18]: https://github.com/bit-team/backintime/releases/tag/v1.0.18 +[1.0.16]: https://github.com/bit-team/backintime/releases/tag/v1.0.16 +[1.0.14]: https://github.com/bit-team/backintime/releases/tag/v1.0.14 +[1.0.12]: https://github.com/bit-team/backintime/releases/tag/v1.0.12 +[1.0.10]: https://github.com/bit-team/backintime/releases/tag/v1.0.10 +[1.0.8]: https://github.com/bit-team/backintime/releases/tag/v1.0.8 +[1.0.6]: https://github.com/bit-team/backintime/releases/tag/v1.0.6 +[1.0.4]: https://github.com/bit-team/backintime/releases/tag/v1.0.4 +[1.0.2]: https://github.com/bit-team/backintime/releases/tag/v1.0.2 +[1.0]: https://github.com/bit-team/backintime/releases/tag/v1.0 +[0.9.26]: https://github.com/bit-team/backintime/releases/tag/v0.9.26 +[0.9.24]: https://github.com/bit-team/backintime/releases/tag/v0.9.24 +[0.9.22.1]: https://github.com/bit-team/backintime/releases/tag/v0.9.22.1 +[0.9.22]: https://github.com/bit-team/backintime/releases/tag/v0.9.22 +[0.9.20]: https://github.com/bit-team/backintime/releases/tag/v0.9.20 +[0.9.18]: https://github.com/bit-team/backintime/releases/tag/v0.9.18 +[0.9.16.1]: https://github.com/bit-team/backintime/releases/tag/v0.9.16.1 +[0.9.16]: https://github.com/bit-team/backintime/releases/tag/v0.9.16 +[0.9.14]: https://github.com/bit-team/backintime/releases/tag/v0.9.14 +[0.9.12]: https://github.com/bit-team/backintime/releases/tag/v0.9.12 +[0.9.10]: https://github.com/bit-team/backintime/releases/tag/v0.9.10 +[0.9.8]: https://github.com/bit-team/backintime/releases/tag/v0.9.8 +[0.9.6]: https://github.com/bit-team/backintime/releases/tag/v0.9.6 +[0.9.4]: https://github.com/bit-team/backintime/releases/tag/v0.9.4 +[0.9.2]: https://github.com/bit-team/backintime/releases/tag/v0.9.2 +[0.9]: https://github.com/bit-team/backintime/releases/tag/v0.9 +[0.8.20]: https://github.com/bit-team/backintime/releases/tag/v0.8.20 +[0.8.18]: https://github.com/bit-team/backintime/releases/tag/v0.8.18 +[0.8.16]: https://github.com/bit-team/backintime/releases/tag/v0.8.16 +[0.8.14]: https://github.com/bit-team/backintime/releases/tag/v0.8.14 +[0.8.12]: https://github.com/bit-team/backintime/releases/tag/v0.8.12 +[0.8.10]: https://github.com/bit-team/backintime/releases/tag/v0.8.10 +[0.8.8]: https://github.com/bit-team/backintime/releases/tag/v0.8.8 +[0.8.6]: https://github.com/bit-team/backintime/releases/tag/v0.8.6 +[0.8.2]: https://github.com/bit-team/backintime/releases/tag/v0.8.2 +[0.8.1]: https://github.com/bit-team/backintime/releases/tag/v0.8.1 +[0.8]: https://github.com/bit-team/backintime/releases/tag/v0.8 +[0.7.4]: https://github.com/bit-team/backintime/releases/tag/v0.7.4 +[0.7.2]: https://github.com/bit-team/backintime/releases/tag/v0.7.2 +[0.7]: https://github.com/bit-team/backintime/releases/tag/v0.7 +[0.6.4]: https://github.com/bit-team/backintime/releases/tag/v0.6.4 +[0.6.2]: https://github.com/bit-team/backintime/releases/tag/v0.6.2 +[0.6]: https://github.com/bit-team/backintime/releases/tag/v0.6 +[0.5.1]: https://github.com/bit-team/backintime/releases/tag/v0.5.1 +[0.5]: https://github.com/bit-team/backintime/releases/tag/v0.5 diff --git a/CHANGES b/CHANGES index 0eb5b4896..093233bd8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,767 +1 @@ -Back In Time - -Version 1.3.2 -* Fix bug: Tests no longer work with Python 3.10 (https://github.com/bit-team/backintime/issues/1175) - -Version 1.3.1 -* bump version, forgot to push branch to Github before releasing - -Version 1.3.0 -* Merge PR: Fix FileNotFoundError exception in mount.mounted, Thanks tatokis (https://github.com/bit-team/backintime/pull/1157) -* Merge PR: qt/plugins/notifyplugin: Fix setting self.user, not local variable, Thanks Zocker1999NET (https://github.com/bit-team/backintime/pull/1155) -* Merge PR: Use Link Color instead of lightGray as not to break theming, Thanks newhinton (https://github.com/bit-team/backintime/pull/1153) -* Merge PR: Match old and new rsync version format, Thanks TheTimeWalker (https://github.com/bit-team/backintime/pull/1139) -* Merge PR: 'TempPasswordThread' object has no attribute 'isAlive', Thanks FMeinicke (https://github.com/bit-team/backintime/pull/1135) -* Merge PR: Keep permissions of an existing mountpoint from being overridden, Thanks bentolor (https://github.com/bit-team/backintime/pull/1058) -* Fix bug: YEAR missing in config (https://github.com/bit-team/backintime/issues/1023) -* Fix bug: SSH module didn't send identification string while checking if remote host is avilable (https://github.com/bit-team/backintime/issues/1030) - -Version 1.2.1 -* Fix bug: TypeError in backintime.py if mount failed while running a snapshot (https://github.com/bit-team/backintime/issues/1005) - -Version 1.2.0 -* Fix bug: Exit code is linked to the wrong status message (https://github.com/bit-team/backintime/issues/906) -* minor changes to allow running BiT inside Docker (https://github.com/bit-team/backintime/pull/959) -* Fix bug: AppName showed 'python3' instead of 'Back In Time' (https://github.com/bit-team/backintime/issues/950) -* Fix bug: configured cipher is not used with all ssh-commands (https://github.com/bit-team/backintime/issues/934) -* remove progressbar on systray icon until BiT has it's own icon (https://github.com/bit-team/backintime/issues/902) -* Fix bug: 'make test' fails because local SSH server is running on non-standard port (https://github.com/bit-team/backintime/issues/945) -* clearify 'nocache' option (https://github.com/bit-team/backintime/issues/857) -* create a config-backup in root dir if backup is encrypted (https://github.com/bit-team/backintime/issues/556) -* Fix bug: 23:00 is missing in the list of every day hours (https://github.com/bit-team/backintime/issues/736) -* Fix bug: ssh-agent output changed (https://github.com/bit-team/backintime/issues/840) -* remove unused and undocumented userscript plugin -* Fix bug: exception on making backintime folder world writeable (https://github.com/bit-team/backintime/issues/812) -* Fix bug: stat free space for snapshot folder instead of backintime folder (https://github.com/bit-team/backintime/issues/733) -* add contextmenu for logview dialog which can copy, exclude and decode lines -* move progressbar under statusbar -* Fix bug: backintime root crontab doesn't run; missinng line-feed 0x0A on last line (https://github.com/bit-team/backintime/issues/781) -* Fix bug: IndexError in inhibitSuspend (https://github.com/bit-team/backintime/issues/772) -* alleviate default exclude [Tt]rash* (https://github.com/bit-team/backintime/issues/759) -* enable high DPI scaling (https://github.com/bit-team/backintime/issues/732) -* Fix bug: polkit CheckAuthorization: race condition in privilege authorization (https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7572) -* Fix bug: OSError when running backup-job from systemd (https://github.com/bit-team/backintime/issues/720) -* Smart Remove try to keep healthy snapshots (https://github.com/bit-team/backintime/issues/703) -* Fix critical bug: restore filesystem-root without 'Full rsync mode' with ACL and/or xargs activated broke whole system (https://github.com/bit-team/backintime/issues/708) -* Fix bug: use current folder if no file is selected in files view (https://github.com/bit-team/backintime/issues/687, https://github.com/bit-team/backintime/issues/685) -* Fix bug: don't reload profile after editing profile name (https://github.com/bit-team/backintime/issues/706) -* Fix bug: Exception in FileInfo -* ask for restore-to path before confirm (https://github.com/bit-team/backintime/issues/678) -* fix 'Back in Time (root)' on wayland (https://github.com/bit-team/backintime/issues/640) -* sort int values in config nummerical instead if alphabetical (https://github.com/bit-team/backintime/issues/175#issuecomment-272941811) -* set timestamp directly after new snapshot (https://github.com/bit-team/backintime/issues/584) -* add shortcut CTRL+H for toggle show hidden files to fileselect dialog (https://github.com/bit-team/backintime/issues/378) -* add 'Edit user-callback' dialog -* Fix bug: failed to restore suid permissions (https://github.com/bit-team/backintime/issues/661) -* redesign restore menu (https://github.com/bit-team/backintime/issues/661) -* Fix bug: on remount user-callback got called AFTER trying to mount (https://github.com/bit-team/backintime/issues/654) -* add ability to disable SSH command- and ping-check (https://github.com/bit-team/backintime/issues/647) -* enable bwlimit for local profiles (https://github.com/bit-team/backintime/issues/646) -* import remote host-key into known_hosts from Settings -* copy public SSH key to remote host from Settings -* create a new SSH key from Settings -* Fix bug: confirm restore dialog has no scroll bar (https://github.com/bit-team/backintime/issues/625) -* Fix bug: DEFAULT_EXCLUDE not deletable (https://github.com/bit-team/backintime/issues/634) -* rename debian package from backintime-qt4 into backintime-qt -* rename paths and methods from *qt4* into *qt* -* rename executable backintime-qt4 into backintime-qt -* new config version 6, rename qt4 keys into qt, add new domain for schedule -* check crontab entries on every GUI startup (https://github.com/bit-team/backintime/issues/129) -* start a new ssh-agent instance only if necessary -* add cli command 'shutdown' (https://github.com/bit-team/backintime/issues/596) -* Fix bug: GUI status bar unreadable (https://github.com/bit-team/backintime/issues/612) -* Fix bug: udev schedule not working (https://github.com/bit-team/backintime/issues/605) -* add cli command 'smart-remove' -* make LogView and Settings Dialog non-modal (https://github.com/bit-team/backintime/issues/608) -* Fix bug: decode path spooled from /etc/mtab (https://github.com/bit-team/backintime/pull/607) -* Fix bug: in snapshots.py, gives more helpful advice if a lock file is present that shouldn't be. (https://github.com/bit-team/backintime/issues/601) -* port to Qt5/pyqt5 (https://github.com/bit-team/backintime/issues/518) -* Fix bug: Fail to create remote snapshot path with spaces (https://github.com/bit-team/backintime/issues/567) -* Fix bug: broken new_snapshot can run into infinite saveToContinue loop (https://github.com/bit-team/backintime/issues/583) -* Recognize changes on previous runs while continuing new snapshots -* Fix bug: udev schedule didn't work with LUKS encrypted drives (https://github.com/bit-team/backintime/issues/466) -* Add pause, resume and stop function for running snapshots (https://github.com/bit-team/backintime/issues/474, https://github.com/bit-team/backintime/issues/195) -* Fix bug: sshMaxArg failed on none default ssh port (https://github.com/bit-team/backintime/issues/581) -* Fix bug: failed if remote host send SSH banner (https://github.com/bit-team/backintime/issues/581) -* Fix bug: incorrect handling of IPv6 addresses (https://github.com/bit-team/backintime/issues/577) -* use rsync to save permissions -* replace os.system calls with subprocess.Popen -* automatically refresh log view if a snapshot is currently running -* Fix bug: Snapshot Log View freeze on big log files (https://github.com/bit-team/backintime/issues/456) -* Fix bug: 'inotify_add_watch failed: file or directory not found' after deleting snapshot -* remove dependency for extended 'find' command on remote host -* make full-rsync mode default, remove the other mode -* Fix bug: a continued snapshot was not incremental (https://github.com/bit-team/backintime/issues/557) -* use rsync to remove snapshots which will give a nice speedup (https://github.com/bit-team/backintime/issues/151) -* open temporary local copy of files instead of original backup on double-click in GUI -* add option to decrypt paths in systray menu with mode ssh-encrypted -* open current log directly from systray icon during taking a snapshot -* add tool-tips to restore menu -* Fix bug: config backup in snapshot had wrong name if using --config option -* add --share-path option -* use Monospace font in logview -* add restore option --only-new -* add button 'Take snapshot with checksums' -* Fix bug: Can't open files with spaces in name (https://github.com/bit-team/backintime/issues/552) -* Fix bug: BIT-root won't start from .desktop file (https://github.com/bit-team/backintime/issues/549) -* Fix bug: Keyring doesn't work with KDE Plasma5 (https://github.com/bit-team/backintime/issues/545) -* Fix bug: Qt4 built-in phrases where not translated (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816197) -* Fix bug: configure ingnore unknown args (https://github.com/bit-team/backintime/issues/547) -* Fix bug: snapshots-list on command-line was not sorted -* Fix bug: SHA256 ssh-key fingerprint was not detected -* change default configure option to --no-fuse-group as Ubuntu >= 12.04 don't need fuse group-membership anymore -* Fix bug: new snapshot did not show up after finished -* Fix bug: TimeLine headers were not correct -* Fix lintian warning: manpage-has-errors-from-man: bad argument name 'P' -* Fix bug: wildcards ? and [] wasn't recognized correctly -* Fix bug: last char of last element in tools.get_rsync_caps got cut off -* Fix bug: TypeError in tools.get_git_ref_hash -* Do not print 'SnapshotID' or 'SnapshotPath' if running 'snapshots-list' command (and other) with '--quiet' -* Remove dependency 'ps' -* Fix bug: don't include empty values in list (https://github.com/bit-team/backintime/issues/521) -* Fix bug: bash-completion doesn't work for backintime-qt4 -* Fix bug: 'make unittest' incorrectly used 'coverage' by default (https://github.com/bit-team/backintime/issues/522) -* Fix bug: pm-utils is deprecated; Remove dependency (https://github.com/bit-team/backintime/issues/519) -* rewrite huge parts of snapshots.py -* remove backwards compatibility to version < 1.0 - -Version 1.1.12 -* Fix bug: remove x-terminal-emulator dependency (https://github.com/bit-team/backintime/issues/515) -* Fix bug: AttributeError in About Dialog (https://github.com/bit-team/backintime/issues/515) - -Version 1.1.10 -* Fix bug: failed to remove empty lock file (https://github.com/bit-team/backintime/issues/505) -* Add Icon 'show-hidden' (https://github.com/bit-team/backintime/issues/507) -* Add Modify for Full System Backup button to settings page, to change some profile settings -* Fix bug: Restore the correct file owner and group fail if they are not present in system (https://github.com/bit-team/backintime/issues/58) -* add get|set_list_value to configfile -* Fix bug: QObject::startTimer error on closing app -* subclass ApplicationInstance in GUIApplicationInstance to reduce redundant code -* speed up app start by adding snapshots to timeline in background thread -* add warning on failed permission restore (https://github.com/bit-team/backintime/issues/58) -* add unittest (thanks to Dorian, Alexandre, Aurélien and Gregory from IAGL) -* Fix bug: FileNotFoundError while starting pw-cache from source -* continue an unfinished new_snapshot if possible (https://github.com/bit-team/backintime/issues/400) -* Fix bug: suppress warning about failed inhibit suspend if run as root (https://github.com/bit-team/backintime/issues/500) -* Fix bug: UI blocked/greyed out while removing snapshot (https://github.com/bit-team/backintime/issues/487) -* Fix bug: pw-cache failed on leftover PID file, using ApplicationInstance now (https://github.com/bit-team/backintime/issues/468) -* Fix bug: failed to parse some arguments (https://github.com/bit-team/backintime/issues/492) -* Fix bug: failed to start GUI if launched from systray icon -* Fix bug: deleted snapshot is still listed in Timeline if using mode SSH (https://github.com/bit-team/backintime/issues/493) -* Fix bug: PermissionError while deleting readonly files on sshfs mounted share (https://github.com/bit-team/backintime/issues/490) -* Add Nautilus-like shortcuts for navigating in file browser (https://github.com/bit-team/backintime/issues/483) -* speed up mounting of SSH+encrypted profiles -* Fix bug: creat new encrypted profiles with encfs >= 1.8.0 failed (https://github.com/bit-team/backintime/issues/477) -* Fix bug: AttributeError in common/tools.py if keyring is missing (https://github.com/bit-team/backintime/issues/473) -* Fix bug: remote rename of 'new_snapshot' folder sometimes isn't recognized locally; rename local now (https://answers.launchpad.net/questions/271792) -* Move source code and bug tracking to GitHub - -Version 1.1.8 -* Fix bug: unlock private SSH key run into 5sec timeout if password is empty -* show current app name and profile ID in syslog (https://launchpad.net/bugs/906213) -* Fix bug: BiT freeze when activate 'Decode path' in 'Snapshot Log View' -* Show 'Profiles' dropdown only in 'Last Log Viewer', add 'Snapshots' dropdown in 'Snapshot Log Viewer' (https://launchpad.net/bugs/1478219) -* Fix bug: empty grey window appears when starting the gui as root (https://launchpad.net/bugs/1493020) -* do not restore permission if they are identical with current permissions -* Fix bug: gnu_find_suffix_support doesn't set back to True (https://launchpad.net/bugs/1487781) -* security issue: do not run user-callback in a shell -* add option to not log user-callback output -* Fix lintian warning dbus-policy-without-send-destination -* apply timestamps-in-gzip.patch from Debian backintime/1.1.6-1 package -* run multiple smart-remove jobs in one screen session (https://launchpad.net/bugs/1487781) -* add error messages if PID file creation fail -* Fix bug: dbus exception if dbus systembus is not running -* Fix bug: depend on virtual package cron-daemon instead of cron for compatiblity with other cron implementations (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=776856) -* Fix bug: wasn't able to start from alternate install dir (https://launchpad.net/bugs/478689) -* Fix bug: wasn't able to start from source dir -* Add Warning about unsupported filesystems -* use native Python code to check mountpoint -* Add expert option for stdout and stderr redirection in cronjobs (https://answers.launchpad.net/questions/270105) -* Fix bug: 'Inhibit Suspend' fails with 'org.freedesktop.PowerManagement.Inhibit' (https://launchpad.net/bugs/1485242) -* Fix bug: No mounting while selecting a secondary profile in the gui (https://launchpad.net/bugs/1481267) -* remove shebang in common/askpass.py and common/create-manpage-backintime-config.py -* Fix bug: fix for bug #1419466 broke crontab on Slackware (https://launchpad.net/bugs/1478576) -* Fix bug: fix for bug #1431305 broke pw-cache on Ubuntu (https://launchpad.net/bugs/1431305) -* Fix bash-complete -* show 'man backintime' on Help; remove link to backintime.le-web.org (https://launchpad.net/bugs/1475995) -* add --debug argument -* Fix bug: Settings accepted empty strings for Host/User/Profile-ID (https://launchpad.net/bugs/1477733) -* Fix bug: IndexError on 'check_remote_commands' due to too long args (https://launchpad.net/bugs/1471930) -* add --local-backup, --no-local-backup and --delete option to restore on command-line (https://launchpad.net/bugs/1467239) -* add 'backup on restore' option to confirm dialog -* add check-config command for command-line -* rewrite command-line argument parsing. Now using argparse -* add expert option SSH command prefix -* Fix bug: Makefile has no uninstall target (https://launchpad.net/bugs/1469152) - -Version 1.1.6 -* show Profile name in systrayicon menu -* Fix bug: encrypted remote backup hangs on 'start encfsctl encode process' (https://launchpad.net/bugs/1455925) -* make own Exceptions a childclass from BackInTimeException -* Fix bug: missing profile.name crashed GUI -* Fix bug: Segmentation fault caused by two QApplication instances (https://launchpad.net/bugs/1463732) -* remove consolekit from dependencies -* Fix bug: no Changes [C] log entries with 'Check for changes' disabled (https://launchpad.net/bugs/1463367) -* Fix bug: some changed options from Settingsdialog where not respected during automatic tests after hitting OK -* Fix bug: python version check fails on python 3.3 (https://launchpad.net/bugs/1463686) -* Specifying the SSH private key whenever ssh is called (https://launchpad.net/bugs/1433682) -* add to in-/exclude directly from mainwindow (https://launchpad.net/bugs/1454856) -* Fix bug: pw-cache didn't start on Mint KDE because of missing stdout and stderr (https://launchpad.net/bugs/1431305) -* add option to run Smart Remove in background on remote host (https://launchpad.net/bugs/1457210) -* Use current profile when starting GUI from Systray -* Fix bug: failed to restore file names with white spaces using CLI (https://launchpad.net/bugs/1435602) -* Fix bug: UnboundLocalError with 'last_snapshot' in _free_space (https://launchpad.net/bugs/1437623) - -Version 1.1.4 -* add option to keep new snapshot with 'full rsync mode' regardless of changes (https://launchpad.net/bugs/1434722) -* Fix bug: wrong quote in 'Save config file' -* Fix bug: Deleting the last snapshot does not update the last_snapshot symlink (https://launchpad.net/bugs/1434724) -* remove base64 encoding for passwords as it doesn't add any security but broke the password process (https://launchpad.net/bugs/1431305) -* add confirm dialog before restoring (https://launchpad.net/bugs/438079) -* Fix bug: Wrong status text in the tray icon (https://launchpad.net/bugs/1429400) -* add option to run only one snapshot at a time -* Fix bug: restore permissions of lots of files made BackInTime unresposive (https://launchpad.net/bugs/1428423) -* Fix bug: failed to restore file owner and group -* cache uuid in config so it doesn't fail if the device isn't pluged in (https://launchpad.net/bugs/1426881) -* add warning about wrong Python version in configure -* prevent snapshots from being removed with restore and delete; show warning if restore and delete filesystem root (https://answers.launchpad.net/questions/262837) -* Fix bug: OSError in free_space; add alternate method to get free space -* add bash-completion -* Fix bug: ugly theme while running as root on Gnome based DEs (https://launchpad.net/bugs/1418447) -* Fix bug: filename with broken charset throwed UnicodeError exception (https://launchpad.net/bugs/1419694) -* use 'crontab' instead of 'crontab -' to read from stdin (https://launchpad.net/bugs/1419466) - -Version 1.1.2 -* sort 'Backup folders' in main window -* save in- and exclude sort order -* use PolicyKit to install Udev rules -* move compression from install to build in Makefiles -* use pkexec to start backintime-qt4 as root - -Version 1.1.0 -* add tooltips for rsync options -* make only one debian/control -* multiselect files to restore (https://launchpad.net/bugs/1135886) -* force run manual snapshots on battery (https://launchpad.net/bugs/861553) -* backup encfs config to local config folder -* apply 'install-docs-move.patch' from Debian package by Jonathan Wiltshire -* add restore option to delete new files during restore (https://launchpad.net/bugs/1371951) -* use flock to prevent two instances running at the same time -* restore config dialog added (https://launchpad.net/bugs/480391) -* inhibit suspend/hibernate while take_snapshot or restore -* use more reliable code for get_user -* implement anacrons functions inside BIT => more flexible schedules and no new timestamp if there was an error -* automatically run in background if started with 'backintime --backup-job' -* fix typos and style warnings in manpages reported by Lintian (https://lintian.debian.org/full/jmw@debian.org.html#backintime_1.0.34-0.1) -* add exclude files by size (https://launchpad.net/bugs/823719) -* remove 'Auto Host/User/Profile-ID' as this is more confiusing than helping -* Fix bug: check procname of pid-locks (https://launchpad.net/bugs/1341414) -* optional run 'rsync' with 'nocache' (https://launchpad.net/bugs/1344528) -* mark invalid exclude pattern with mode ssh-encrypted -* make Settingsdialog tabs scrollable -* remove colon (:) restriction in exclude pattern -* prevent starting new snapshot if restore is running -* Fix bug: Port check failed on IPv6 (https://launchpad.net/bugs/1361634) -* add top-level directory for tarball (https://launchpad.net/bugs/1359076) -* add more user-callback events (on App start and exit, on mount and unmount) -* add context menu to files view -* remove snapshots from commandline -* multi selection in timeline => remove multiple snapshots with one click -* print warning if started with sudo -* add more default exclude; remove [Cc]ache* from exclude -* Fix bug: 'inotify_add_watch failed' while closing BIT -* add option for custom rsync-options -* add ProgressBar for rsync -* add progress for smart-remove -* remove old status-bar message after a snapshot crashed. -* ask to include symlinks target instead link (https://launchpad.net/bugs/1117709) -* port to Python 3.x -* returncode >0 if there was an error (https://launchpad.net/bugs/1040995) -* Enable user-callback script to cancel a backup by returning a non-zero exit code. -* merge backintime-notify into backintime-qt4 -* add --gksu/--gksudo arg to qt4/configure -* remember last path for each profile (https://bugs.launchpad.net/bugs/1254870) -* sort include and exclude list (https://bugs.launchpad.net/bugs/1193149) -* Timeline show tooltip 'Last check' -* Fix bug: systray icon didn't show up (https://bugs.launchpad.net/backintime/+bug/658424) -* show hidden files in FileDialog (https://bugs.launchpad.net/backintime/+bug/995925) -* add button text for all buttons (https://bugs.launchpad.net/backintime/+bug/992020) -* add shortcuts (https://bugs.launchpad.net/backintime/+bug/686694) -* add menubar (https://bugs.launchpad.net/backintime/+bug/528851) -* port KDE4 GUI to pure Qt4 to replace both KDE4 and Gnome GUI - -Version 1.0.40 -* use fingerprint to check if ssh key was unlocked correctly (https://answers.launchpad.net/questions/256408) -* add fallback method to get UUID (https://answers.launchpad.net/questions/254140) -* Fix bug: 'Attempt to unlock mutex that was not locked'... this time for good - -Version 1.0.38 -* Fix bug: 'Attempt to unlock mutex that was not locked' in gnomeplugin (https://answers.launchpad.net/questions/255225) -* compare os.path.realpath instead of os.stat to get devices UUID -* Fix bug: housekeeping by gnome-session-daemon might delete backup and original data (https://bugs.launchpad.net/bugs/1374343) -* Fix bug: Type Error in 'backintime --decode' (https://bugs.launchpad.net/bugs/1365072) -* Fix bug: take_snapshot didn't wait for snapshot folder come available if notifications are disabled (https://bugs.launchpad.net/bugs/1332979) - -Version 1.0.36 -* remove UbuntuOne from exclude (https://bugs.launchpad.net/bugs/1340131) -* Gray out 'Add Profile' if 'Main Profile' isn't configured yet (https://bugs.launchpad.net/bugs/1335545) -* Don't check for fuse group-membership if group doesn't exist -* Fix bug: backintime-kde4 as root failed to load ssh-key (https://bugs.launchpad.net/bugs/1276348) -* Fix bug: kdesystrayicon.py crashes because of missing environ (https://bugs.launchpad.net/bugs/1332126) -* Fix bug: OSError if sshfs/encfs is not installed (https://bugs.launchpad.net/bugs/1316288) -* Fix bug: TypeError in config.py check_config() (https://bugzilla.redhat.com/show_bug.cgi?id=1091644) -* Fix bug: unhandled exception in create_last_snapshot_symlink() (https://bugs.launchpad.net/bugs/1269991) -* disable keyring for root - -Version 1.0.34 -* sync/flush all disks before shutdown (https://bugs.launchpad.net/bugs/1261031) -* Fix bug: BIT running as root shutdown after snapshot, regardless of option checked (https://bugs.launchpad.net/bugs/1261022) - -Version 1.0.32 -* Fix bug: cron scheduled snapshots won't start with 1.0.30 - -Version 1.0.30 -* scheduled and manual snapshots use --config -* make configure scripts portable (https://bugs.launchpad.net/backintime/+bug/377429) -* Fix bug: udev rule doesn't finish (https://bugs.launchpad.net/backintime/+bug/1249466) -* add symlink last_snapshot (https://bugs.launchpad.net/backintime/+bug/787118) -* add virtual package backintime-kde for PPA -* Fix multiple errors in PPA build process; reorganise updateversion.sh -* Fix bug: Mate and xfce desktop didn't show systray icon (https://bugs.launchpad.net/backintime/+bug/658424/comments/31) -* add option to run rsync with 'nice' or 'ionice' on remote host (https://bugs.launchpad.net/backintime/+bug/1240301) -* add Shutdown button to shutdown system after snapshot has finished (https://bugs.launchpad.net/backintime/+bug/838742) -* Fix bug: Ubuntu Lucid dosn't provide SecretServiceKeyring (https://bugs.launchpad.net/backintime/+bug/1243911) -* wrap long lines for syslog -* Fix bug: 'gksu backintime-gnome' failed with dbus.exceptions.DBusException - -Version 1.0.28 -* remove config on 'apt-get purge' -* add more options for configure scripts; update README -* add udev schedule (run BIT as soon as the drive is connected) -* Fix bug: AttributeError with python-keyring>1.6.1 (https://bugs.launchpad.net/backintime/+bug/1234024) -* Fix bug: TypeError: KDirModel.removeColumns() is a private method in kde4/app.py (https://bugs.launchpad.net/backintime/+bug/1232694) -* add '--checksum' commandline option (https://bugs.launchpad.net/backintime/+bug/886021) -* Fix bug: sshfs mount disconnect after a while due to some firewalls (add ServerAliveInterval) (https://answers.launchpad.net/backintime/+question/235685) -* Fix bug: Ping fails if ICMP is disabled on remote host (https://bugs.launchpad.net/backintime/+bug/1226718) -* Fix bug: KeyError in getgrnam if there is no 'fuse' group (https://bugs.launchpad.net/backintime/+bug/1225561) -* Fix bug: anacrontab won't work with profilename with spaces (https://bugs.launchpad.net/backintime/+bug/1224620) -* Fix bug: NameError in tools.move_snapshots_folder (https://bugs.launchpad.net/backintime/+bug/871466) -* Fix bug: KPassivePopup is not defined (https://bugs.launchpad.net/backintime/+bug/871475) -* multi selection for include and exclude list (https://bugs.launchpad.net/backintime/+bug/660753) -* Fix bug: ValueError while reading pw-cache PID (https://answers.launchpad.net/backintime/+question/235407) - -Version 1.0.26 -* add feature: keep min free inodes -* roll back commit 836.1.5 (check free-space on ssh remote host): statvfs DOES work over sshfs. But not with quite outdated sshd -* add daily anacron schedule -* add delete button and 'list only equal' in Snapshot dialog; multiSelect in snapshot list -* add manpage backintime-config and config-examples -* Fix bug: Restore makes files public during the operation -* Fix bug: Cannot keep modifications to cron (https://bugs.launchpad.net/backintime/+bug/698106) -* add feature: restore from command line; add option --config -* Fix bug: cannot stat 'backintime-kde4-root.desktop.kdesudo' (https://bugs.launchpad.net/backintime/+bug/696659) -* Fix bug: unreadable dark KDE color schemes (https://bugs.launchpad.net/backintime/+bug/1184920) -* use 'ps ax' to check if 'backintime --pw-cache' is still running -* mount after locking, unmount before unlocking in take_snapshot -* Fix bug: permission denied if remote uid wasn't the same as local uid -* add option --bwlimit for rsync -* redirect logger.error and .warning to stderr; new argument --quiet -* deactivate 'Save Password' if no keyring is available -* use Password-cache for user-input too -* handle two Passwords -* add 'SSH encrypted': mount / with encfs reverse and sync encrypted with rsync. EXPERIMENTEL! -* add 'Local encrypted': mount encfs - -Version 1.0.24 -* hide check_for_canges if full_rsync_mode is checked -* DEFAULT_EXCLUDE system folders with /foo/* so at least the folder itself will backup -* DEFAULT_EXCLUDE /run; exclude MOUNT_ROOT with higher priority and not with DEFAULT_EXCLUDE anymore -* Fix bug: 'CalledProcessError' object has no attribute 'strerror' -* Fix bug: quote rsync remote path with spaces -* 'Save Password' default off to avoid problems with existing profiles -* if restore uid/gid failed try to restore at least gid -* SSH need to store permissions in seperate file with "Full rsync mode" because remote user might not be able to store ownership -* Fix bug: restore permission failed on "Full rsync mode" -* Fix bug: glib.GError: Unknown internal child: selection -* Fix bug: GtkWarning: Unknown property: GtkLabel.margin-top -* Fix bug: check keyring backend only if password is needed -* switch to 'find -exec cmd {} +' (https://bugs.launchpad.net/backintime/+bug/1157639) -* change all indent tabs to 4 spaces - -Version 1.0.22 -* check free-space on ssh remote host (statvfs didn't work over sshfs) -* Add Password storage mode ssh -* Add "Full rsync mode" (can be faster but ...) -* Fix bug: "Restore to..." failed due to spaces in directory name (https://bugs.launchpad.net/backintime/+bug/1096319) -* Fix bug: host not found in known_hosts if port != 22 (https://bugs.launchpad.net/backintime/+bug/1130356) -* Fix bug: sshtools.py used not POSIX conform conditionals - -Version 1.0.20 -* Fix bug: restore remote path with spaces using mode ssh returned error - -Version 1.0.18 -* Fix packages: man & translations -* Fix bug: https://bugs.launchpad.net/backintime/+bug/1077446 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/1078979 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/1079479 -* Map multiple arguments for gettext so they can be rearranged by translators - -Version 1.0.16 -* Fix a package dependecy problem ... this time for good (https://bugs.launchpad.net/backintime/+bug/1077446) - -Version 1.0.14 -* Fix a package dependecy problem - -Versoin 1.0.12 -* Add links to: website, documentation, report a bug, answers, faq -* Use libnotify for gnome/kde4 notifications instead of gnome specific libraries -* Fix bug: https://bugs.launchpad.net/backintime/+bug/1059247 -* Add more schedule options: every 30 min, every 2 hours, every 4 hours, every 6 hours & every 12 hours -* Add generic mount-framework -* Add mode 'SSH' for backups on remote host using ssh protocol. -* Fix bug: wrong path if restore system root -* Fix bug: glade (xml) files did not translate -* Fix bug: https://bugs.launchpad.net/backintime/+bug/1073867 - -Version 1.0.10 -* Add "Restore to ..." in replacement of copy (with or without drag & drop) because copy don't restore user/group/rights -Version 1.0.8 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/723545 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/705237 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/696663 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/671946 - -Version 1.0.6 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/676223 -* Smart remove: configurable options (https://bugs.launchpad.net/backintime/+bug/406765) -* Fix bug: https://bugs.launchpad.net/backintime/+bug/672705 - -Version 1.0.4 -* SettingsDialog: show highly recommended excludes -* Fix bug: https://bugs.launchpad.net/backintime/+bug/664783 -* Option to use checksum to detect changes (https://bugs.launchpad.net/backintime/+bug/666964) -* Option to select log verbosity (https://bugs.launchpad.net/backintime/+bug/664423) -* Gnome: use gloobus-preview if installed - -Version 1.0.2 -* reduce log file (no more duplicate "Compare with..." lines) -* declare backintime-kde4 packages as a replacement of backintime-kde - -Version 1.0 -* add '.dropbox*' to default exclude patterns (https://bugs.launchpad.net/backintime/+bug/628172) -* add option to take a snapshot at every boot (https://bugs.launchpad.net/backintime/+bug/621810) -* fix xattr -* add continue on errors (https://bugs.launchpad.net/backintime/+bug/616299) -* add expert options: copy unsafe links & copy links -* "user-callback" replace "user.callback" and receive profile informations -* documentation: on-line only (easier to maintain) -* add error log and error log view dialog (Gnome & KDE4) -* merge with: lp:~dave2010/backintime/minor-edits -* merge with: lp:~mcfonty/backintime/unique-snapshots-view -* fix bug: https://bugs.launchpad.net/backintime/+bug/588841 -* fix bug: https://bugs.launchpad.net/backintime/+bug/588215 -* fix bug: https://bugs.launchpad.net/backintime/+bug/588393 -* fix bug: https://bugs.launchpad.net/backintime/+bug/426400 -* fix bug: https://bugs.launchpad.net/backintime/+bug/575022 -* fix bug: https://bugs.launchpad.net/backintime/+bug/571894 -* fix bug: https://bugs.launchpad.net/backintime/+bug/553441 -* fix bug: https://bugs.launchpad.net/backintime/+bug/550765 -* fix bug: https://bugs.launchpad.net/backintime/+bug/507246 -* fix bug: https://bugs.launchpad.net/backintime/+bug/538855 -* fix bug: https://bugs.launchpad.net/backintime/+bug/386230 -* fix bug: https://bugs.launchpad.net/backintime/+bug/527039 -* reduce memory usage during compare with previous snapshot process -* fix bug: https://bugs.launchpad.net/backintime/+bug/520956 -* fix bug: https://bugs.launchpad.net/backintime/+bug/520930 -* fix bug: https://bugs.launchpad.net/backintime/+bug/521223 -* custom backup hour (for daily backups or mode): https://bugs.launchpad.net/backintime/+bug/507451 -* fix bug: https://bugs.launchpad.net/backintime/+bug/516066 -* fix bug: https://bugs.launchpad.net/backintime/+bug/512813 -* smart remove was slightly changed (https://bugs.launchpad.net/backintime/+bug/502435) -* fix bug: https://bugs.launchpad.net/backintime/+bug/503859 -* make backup on restore optional -* fix bug: https://bugs.launchpad.net/backintime/+bug/501285 -* add ionice support for user/cron backup process -* fix bug: https://bugs.launchpad.net/backintime/+bug/493558 -* fix bug that could cause "ghost" folders in snapshots (LP: 406092) -* fix bug that converted / into // (LP: #455149) -* fix bug: https://bugs.launchpad.net/backintime/+bug/441628 -* remove "schedule per included directory" (profiles do that) (+ bug LP: #412470) -* fig bug: https://bugs.launchpad.net/backintime/+bug/489380 -* fix bug: https://bugs.launchpad.net/backintime/+bug/489319 -* fix bug: https://bugs.launchpad.net/backintime/+bug/447841 -* fix bug: https://bugs.launchpad.net/backintime/+bug/412695 -* update Slovak translation (Tomáš Vadina ) -* multiple profiles support -* GNOME: fix notification -* backintime snapshot folder is restructured to ../backintime/machine/user/profile_id/ -* added the possibility to include other snapshot folders within a profile, it can only read those, there is not a GUI implementation yet -* added a tag suffix to the snapshot_id, to avoid double snapshot_ids -* added a desktop file for kdesu and a test if kdesu or kdesudo should be used (LP: #389988) -* added expert option to disable snapshots when on battery (LP: #388178) -* fix bug handling big files by the GNOME GUI (LP: #409130) -* fix bug in handling of & characters by GNOME GUI (LP: #415848) -* fix a security bug in chmods before snapshot removal (LP: #419774) -* snapshots are stored entirely read-only (LP: #386275) -* fix exclude patterns in KDE4 (LP:#432537) -* fix opening german files with external applications in KDE (LP: #404652) -* changed default exclude patterns to caches, thumbnails, trashbins, and backups (LP: #422132) -* write access to snapshot folder is checked & change to snapshot version 2 (LP: #423086) -* fix small bugs (a.o. LP: #474307) -* Used a more standard crontab syntax (LP: #409783) -* Stop the "Over zealous removal of crontab entries" (LP: #451811) - -Version 0.9.26 -* update translations from Launchpad -* Fix a bug in smart-remove algorithm (https://bugs.launchpad.net/backintime/+bug/376104) -* Fix bug: https://bugs.launchpad.net/backintime/+bug/374477 -* Fix bug: https://bugs.launchpad.net/backintime/+bug/375113 -* update German translation (Michael Wiedmann ) -* add '--no-check' option to configure scripts -* use only 'folder' term (more consistent with GNOME/KDE) -* add 'expert option': enable/disable nice for cron jobs -* GNOME & KDE4: refresh snapshots button force files view to update too -* you can include a backup parent directory (backup directory will auto-exclude itself) -* fix some small bugs - -Version 0.9.24 -* update translations -* KDE4: fix python string <=> QString problems -* KDE4 FilesView/SnapshotsDialog: ctrl-click just select (don't execute) -* KDE4: fix crush after "take snapshot" process (https://bugs.launchpad.net/backintime/+bug/366241) -* store basic permission in a special file so it can restore them correctly (event from NTFS) -* add config version -* implement Gnome/KDE4 systray icons and user.callback as plugins -* reorganize code: common/GNOME/KDE4 -* GNOME: break the big glade file in multiple file -* backintime is no longer aware of 'backintime-gnome' and 'backintime-kde4' - (you need run 'backintime-gnome' for GNOME version and - 'backintime-kde4' for KDE4 version) - -Version 0.9.22.1 -* fix French translation - -Version 0.9.22 -* update translations from Launchpad -* KDE4: fix some translation problems -* remove --safe-links for save/restore (this means copy symlinks as symlinks) -* update German translation (Michael Wiedmann ) -* create directory now use python os.makedirs (replace use of mkdir command) -* KDE4: fix a crush related to QString - python string conversion -* GNOME & KDE4 SettingsDialog: if schedule automatic backups per directory is set, global schedule is hidden -* GNOME FilesView: thread "*~" files (backup files) as hidden files -* GNOME: use gtk-preferences icon for SettingsDialog (replace gtk-execute icon) -* expert option: $XDG_CONFIG_HOME/backintime/user.callback (if exists) is called a different steps - of a "take snapshot" process (before, after, on error, is a new snapshot was taken). -* add more command line options: --snapshots-list, --snapshots-list-path, --last-snapshot, --last-snapshot-path -* follow FreeDesktop directories specs: - $XDG_DATA_HOME (default: $HOME/.local/share) to store app.lock files - $XDG_CONFIG_HOME (default: $HOME/.config) to save settings -* new install system: use more common steps (./configure; make; sudo make install) - -Version 0.9.20 -* smart remove: fix an important bug and make it more verbose in syslog -* update Spanish translation (Francisco Manuel García Claramonte ) - -Version 0.9.18 -* update translations from Launchpad -* update Slovak translation (Tomáš Vadina ) -* update French translation (Michel Corps ) -* update German translation (Michael Wiedmann ) -* GNOME bugfix: fix a crush in files view for files with special characters (ex: "a%20b") -* GNOME SettingsDialog bugfix: if snapshots path is a new created folder, snapshots navigation (files view) don't work -* update doc -* GNOME & KDE4 MainWindow: Rename "Places" list with "Snapshots" -* GNOME SettingsDialog bugfix: modify something, then press cancel. If you reopen the dialog it show wrong values (the ones before cancel) -* GNOME & KDE4: add root mode menu entries (use gksu for gnome and kdesudo for kde) -* GNOME & KDE4: MainWindow - Files view: if the current directory don't exists in current snapshot display a message -* SettingDialog: add an expert option to enable to schedule automatic backups per directory -* SettingDialog: schedule automatic backups - if the application can't find crontab it show an error -* SettingDialog: if the application can't write in snapshots directory there should be an error message -* add Polish translation (Paweł Hołuj ) -* add cron in common package dependencies -* GNOME & KDE4: rework settings dialog -* SettingDialog: add an option to enable/disable notifications - -Version 0.9.16.1 -* fix a bug/crush for French version - -Version 0.9.16 -* update Spanish translation (Francisco Manuel García Claramonte ) -* add Slovak translation (Tomáš Vadina ) -* update Swedish translation (Niklas Grahn ) -* update French translation (Michel Corps ) -* update German translation (Michael Wiedmann ) -* update Slovenian translation (Vanja Cvelbar ) -* don't show the snapshot that is being taken in snapshots list -* GNOME & KDE4: when the application starts and snapshots directory don't exists show a messagebox -* give more information for 'take snapshot' progress (to prove that is not blocked) -* MainWindow: rename 'Timeline' column with 'Snapshots' -* when it tries to take a snapshot if the snapshots directory don't exists - (it is on a removable drive that is not plugged) it will notify and wait maximum 30 seconds - (for the drive to be plugged) -* GNOME & KDE4: add notify if the snapshots directory don't exists -* KDE4: rework MainWindow - -Version 0.9.14 -* update German translation (Michael Wiedmann ) -* update Swedish translation (Niklas Grahn ) -* update Spanish translation (Francisco Manuel García Claramonte ) -* update French translation (Michel Corps ) -* GNOME & KDE4: rework MainWindow -* GNOME & KDE4: rework SettingsDialog -* GNOME & KDE4: add "smart" remove - -Version 0.9.12 -* bug fix: now if you include ".abc" folder and exclude ".*", ".abc" will be saved in the snapshot -* KDE4: add help -* add Slovenian translation (Vanja Cvelbar ) -* bug fix (GNOME): bookmarks with special characters - -Version 0.9.10 -* add Swedish translation (Niklas Grahn ) -* KDE4: drop and drop from backintime files view to any file manager -* bug fix: fix a segfault when running from cron - -Version 0.9.8 -* update Spanish translation (Francisco Manuel García Claramonte ) -* bug fix: unable to restore files that contains space char in their name -* unsafe links are ignored (that means that a link to a file/directory outside of include directories are ignored) -* KDE4: add copy to clipboard -* KDE4: sort files by name, size or date -* cron 5/10 minutes: replace mutiple lines with a single crontab line using divide (*/5 or */10) -* cron: when called from cron redirect output (stdout & stderr) to /dev/null - -Version 0.9.6 -* update Spanish translation (Francisco Manuel García Claramonte ) -* update German translation (Michael Wiedmann ) -* GNOME: update docbook -* KDE4: add snapshots dialog -* GNOME & KDE4: add update snapshots button -* GNOME: handle special folders icons (home, desktop) - -Version 0.9.4 -* update German translation (Michael Wiedmann ) -* gnome: better handling of 'take snapshot' status icon -* KDE4 (>= 4.1): first version (not finished) -* update man - -Version 0.9.2 -* update Spanish translation (Francisco Manuel García Claramonte ) -* update German translation (Michael Wiedmann ) -* bug fix: if you add "/a" in include directories and "/a/b" in exclude patterns, "/a/b*" items - are not excluded -* replace diff with rsync to check if a new snapshot is needed -* code cleanup -* add show hidden & backup files toggle button for files view -* bug fix: it does not include ".*" items even if they are not excluded - (the items was included but not showed because hidden & backup files was never displayed - in files view in previous versions) - -Version 0.9 -* update Spanish translation (Francisco Manuel García Claramonte ) -* make deb packages more debian friendly (thanks to Michael Wiedmann ) -* update German translation (Michael Wiedmann ) -* bug fix: when you open snapshots dialog for the second time ( or more ) and you make a diff - it will make the diff on the file for the first dialog ( all previous dialogs ) and then for - the current one -* better separation between common and gnome specific files and - divide backintime package in backintime-common & backintime-gnome - (this will allow me to write other GUI front-ends like KDE4 or KDE) -* code cleanup - -Version 0.8.20 -* bug fix: sorting files/directories by name is now case insensitive -* getmessages.sh: ignore "gtk-" items (this are gtk stock item ids and should not be changed) - -Version 0.8.18 -* update man/docbook -* add sort columns in MainWindow/FileView (by name, by size or by date) and SnapshotsDialog (by date) -* fix German translation (Michael Wiedmann ) - -Version 0.8.16 -* add Drag & Drop from MainWindow:FileView/SnapshotsDialog to Nautilus -* update German translation (Michael Wiedmann ) - -Version 0.8.14 -* add more command line parameters ( --version, --snapshots, --help ) -* fix a crush for getting info on dead symbolic links -* when taking a new backup based on the previous one don't copy the previous extra info (ex: name) -* copy unsafe links when taking a snapshot - -Version 0.8.12 -* add German translation (Michael Wiedmann ) -* add SnapshotNameDialog -* add Name/Remove snapshot in main toolbar -* change the way it detects if the mainwindow is the ative window (no dialogs) -* toolbars: show icons only -* update Spanish translation (Francisco Manuel García Claramonte ) - -Version 0.8.10 -* SnapshotsDialog: add right-click popup-menu and a toolbar with copy & restore buttons -* use a more robust backup lock file -* log using syslog -* fix a small bug in copy to clipboard -* update Spanish translation (Francisco Manuel García Claramonte ) - -Version 0.8.8 -* SnapshotsDialog: add diff -* update Spanish translation (Francisco Manuel García Claramonte ) - -Version 0.8.6 -* fix change backup path crush -* add SnapshotsDialog - -Version 0.8.2 -* add right-click menu in files list: open (using gnome-open), copy (you can paste in Nautilus), restore (for snapshots only) -* add Copy toolbar button for files list - -Version 0.8.1 -* add every 5/10 minutes automatic backup - -Version 0.8 -* don't show backup files (*~) -* add backup files to default exclude patterns (*~) -* makedeb.sh: make a single package with all languages included -* install.sh: install all languages -* add English manual (man) -* add English help (docbook) -* add help button in main toolbar -* the application can be started with a 'path' to a folder or file as command line parameter -* when the application start, if it is already runnig pass its command line to the first instance (this allow a basic integration with file-managers - see README) -* bug fix: when the application was started a second time it raise the first application's window but not always focused - -Version 0.7.4 -* if there is already a GUI instance running raise it -* add Spanish translation (Francisco Manuel García Claramonte ) - -Version 0.7.2 -* better integration with gnome icons (use mime-types) -* remember last path -* capitalize month in timeline (bug in french translation) - -Version 0.7 -* fix cron segfault -* fix a crush when launched the very first time (not configured) -* multi-lingual support -* add French translation - -Version 0.6.4 -* remove About & Settings dialogs from the pager -* allow only one instance of the application - -Version 0.6.2 -* remember window position & size - -Version 0.6 -* when it make a snapshot it display an icon in systray area -* the background color for group items in timeline and places reflect more - the system color scheme -* during restore only restore button is grayed ( even if everything is blocked ) - -Version 0.5.1 -* add size & date columns in files view -* changed some texts - -Version 0.5 -* This is the first release. +Moved to CHANGELOG.md. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..c76b59d69 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,557 @@ + +# How to contribute to _Back In Time_ + +😊 **Thanks for taking the time to contribute.** 😊 + +🟢 **Contributions can be much more than code.** 🟢 + +The maintenance team welcomes all types of contributions. No contribution will +be rejected solely because it doesn't meet our quality standards, guidelines, +or rules. Every contribution is reviewed, and if necessary, improved in +collaboration with the maintenance team. New contributors who may need +assistance or are less experienced are warmly welcomed and will be mentored by +the maintenance team upon request. + +There are many ways to contribute beyond coding: +[translating the project](https://github.com/bit-team/backintime/issues/1915), +performing manual tests, analyzing and reproducing +[bugs](https://github.com/bit-team/backintime/issues), reviewing and testing +[pull requests](https://github.com/bit-team/backintime/pulls), +providing feedback on new features, designing an +[application logo](https://github.com/bit-team/backintime/issues/1961), or +reviewing the documentation and suggesting improvements. +🚀 Every contribution helps the project grow! 🚀 + +> [!TIP] +> Don't forget to introduce yourself if you are new to the project. Also, read +> this document carefully. + +# Index + + +- [Before you start](#before-you-start) +- [Best practice and recommendations](#best-practice-and-recommendations) +- [Resources & Further Readings](#resources--further-readings) +- [Build & Install](#build--install) + - [Dependencies](#dependencies) + - [Build and install via `make` system + (recommended)](#build-and-install-via-make-system-recommended) +- [Testing](#testing) + - [SSH](#ssh) +- [What happens after you opened a Pull Request (PR)?](#what-happens-after-you-opened-a-pull-request-pr) +- [Instructions about translation](#instructions-about-translation) + - [Terminology](#terminology) + - [General recommendations for developers](#general-recommendations-for-developers) + - [Consider Right-to-Left (RTL) and Bidirectional (BIDI) languages](#consider-right-to-left-rtl-and-bidirectional-bidi-languages) + - [Be aware of shortcut indicators and possible duplicates](#be-aware-of-shortcut-indicators-and-possible-duplicates) + - [Treat other translators work with respect](#treat-other-translators-work-with-respect) +- [Strategy Outline](#strategy-outline) +- [Licensing of contributed material](#licensing-of-contributed-material) +- [Quick technical guide](#quick-technical-guide) + + +# Before you start +- **Read this document carefully.** +- Remember that this project is maintained by volunteers in their free time – + human beings just like you. +- [You should be a user of this software](FAQ.md#can-i-contribute-without-using-the-software). +- [Introduce yourself](FAQ.md#why-do-i-need-to-introduce-myself). An introduction will help to distinguish you from AI generated contributions. Otherwise the risk is high that your PR is closed. +- Take the responsibility for your contribution. The [Generative AI policy](https://developer.joomla.org/generative-ai-policy.html) of the _Joomla_ project might give you deeper insights on that. +- Check out the [FAQ entries about contributing to the project](FAQ.md#project--contributing--more). +- And finally: Don't refuse to read this document carefully! + +# Best practice and recommendations +If possible, please consider the following best practices. This will +reduce the workload of the maintainers and increase the chances of your +pull request being accepted. + +- Follow [PEP 8](https://peps.python.org/pep-0008/) as a minimal Style Guide + for Python Code. +- About strings: + - Prefer _single quotes_ (e.g. `'Hello World'`) over _double qutoes_ + (e.g. `"Hello World"`). Exceptions are when single quotes contained in the + string (e.g. `"Can't unmount"`). + - Enclose translatable strings like this: `_('Translate me')`. Find more + details in our + [localization docu](doc/maintain/2_localization.md#instructions-for-the-translation-process). +- For docstrings follow [Google Style Guide](https://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html) + (see our own [HOWTO about doc generation](doc/maintain/1_doc_howto.md)). +- Avoid the use of automatic formatters like `black` but mention the use of + them when opening a pull request. +- Run unit tests before you open a pull request. Read [Testing](#testing) + for further details. +- Try to create new unit tests if appropriate. Use the style of regular Python + `unittest` rather than `pytest`. If you know the difference, please try to + follow the _Classical (aka Detroit) school_ instead of _London (aka mockist) + school_. +- See recommendations about [how to handle translatable strings](doc/maintain/2_localization.md#instructions-for-the-translation-process). + +# Resources & Further readings + +- [Mailing list _bit-dev_](https://mail.python.org/mailman3/lists/bit-dev.python.org/) + +- [Translations](https://translate.codeberg.org/engage/backintime) are done on a separate platform. +- [HowTo's and maintenance](doc/maintain/README.md) +- [FAQ entries about contributing to the project](FAQ.md#project--contributing--more). +- Further readings + - [contribution-guide.org](https://www.contribution-guide.org) + - [How to submit a contribution (opensource.guide)](https://opensource.guide/how-to-contribute/#how-to-submit-a-contribution) + - [mozillascience.github.io/working-open-workshop/contributing](https://mozillascience.github.io/working-open-workshop/contributing) + +# Build & Install + +This section describes how to build and install _Back In Time_ in preparation +of your own contributions. It is assumed that you `git clone` this repository +first. + +## Dependencies + +The following dependencies are based on _Debian GNU/Linux_. Please [open an +Issue](https://github.com/bit-team/backintime/issues/new/choose) if something +is missing. If you use another GNU/Linux distribution, please install the +corresponding packages. Even if some packages are available from PyPi stick to +the packages provided by the official repository of your GNU/Linux +distribution. + +* Runtime dependencies for the CLI + + - `python3` (>= 3.13) + - `rsync` + - `cron-daemon` + - `openssh-client` + - `sshfs` + - `python3-keyring` + - `python3-dbus` + - `python3-packaging` + - Recommended + - `encfs` + - `gocryptfs` + +* Runtime dependencies for the GUI + + - `x11-utils` + - `python3-pyqt6` (Do not use the version from _PyPi_ via `pip`) + - `python3-dbus.mainloop.pyqt6` (not available from _PyPi_ via `pip`) + - `python3-pyqt6.qtsvg` + - `pkexec` + - `bash` (Used by root mode starter script `qt/backintime-qt_polkit`) + - `polkitd` + - `qttranslations6-l10n` or alternative package name `qt6-translations-l10n` + - `qtwayland6` (if Wayland is used as display server instead of X11) or + alternative package name `qt6-wayland` + - Recommended + - For SSH key storage **one** of these packages + - `python3-secretstorage` + - `python3-keyring-kwallet` + - `python3-gnomekeyring` + - For diff-like comparing files between backups **one** of these + packages + - `kompare` + - or `meld` + +* Build and testing dependencies + - All CLI runtime dependencies including the recommended + - All GUI runtime dependencies including the recommended + - `build-essential` + - `gzip` + - `gettext` + - `python3-pyfakefs` (>= 5.7) + - `asciidoctor` + - Optional but recommended: + - `pylint` (>= 4.0.0) + - `flake8` + - `ruff` (>= 0.15.0) + - `codespell` + - `reuse` (>= 4.0.0) + +* Dependencies to build documentation + - All runtime, build, testing dependencies including the recommended + - `mkdocs` to build HTML user manual + - `mkdocs-material` + - `pandoc` to convert changelog from Markdown into HTML + +## Build and install via `make` system (recommended) + +> [!IMPORTANT] +> Install [Dependencies](#dependencies) before you build and install. + +Remember that _Back In Time_ does consist of two packages, which must be built +and installed separately accordingly. + +* Command line tool + 1. `cd common` + 2. `./configure && make` + 3. Run unit tests via `python -m unittest` or `pytest`. + 4. `sudo make install` + +* Qt GUI + 1. `cd qt` + 2. `./configure && make` + 3. Run unit tests via `python -m unittest` or `pytest`. + 4. `sudo make install` + +You can use optional arguments to `./configure` for creating a Makefile. +See `common/configure --help` and `qt/configure --help` for details. + +# Testing +> [!IMPORTANT] +> Remember to **manually** test _Back In Time_ and not rely solely on +> the automatic test suite. See section +> [Manual testing](doc/maintain/BiT_release_process.md#manual-testing---recommendations) +> about recommendations how to perform such tests. + +After [building and installing](#build--install), run the test suite. Feel free +to use Python's own `unittest` module or `pytest` as a test runer. +Since _Back In Time_ consists of two components, `common` and `qt`, +the tests are segregated accordingly. + + $ cd common + $ pytest + +Or + + $ cd qt + $ pytest + +> [!IMPORTANT] +> Even if `pytest` is used as test runner, don't write tests in +> `pytest`-style. Stick to the good old `unittest`-style. This is a project +> rule, taking maintainability into account. + +## SSH + +Some tests require an available SSH server. Those tests get skipped if no SSH +server is available. The goal is to log into the SSH server on your local +computer via `ssh localhost` without using a password: + +- Generate an RSA key pair executing `ssh-keygen`. Use the default file name + and don't use a passphrase for the key. +- Populate the public key to the server executing `ssh-copy-id`. +- Make the `ssh` instance run. +- The port `22` (SSH default) should be available. +- _Authorize_ the key with `$ ssh localhost` and insert your user accounts + password. + +To test the connection just execute `ssh localhost` and you should see an +SSH shell **without** being asked for a password. + +For detailed setup instructions see the +[how to setup openssh for unit tests](doc/maintain/3_How_to_set_up_openssh_server_for_ssh_unit_tests.md). + +# What happens after you opened a Pull Request (PR)? +In short: +1. The maintenance team will review your PR in days or weeks. +2. Modifications may be requested, and the PR will eventually be approved. +3. One of two labels will be added to the PR: + - [PR: Merge after creative-break](https://github.com/bit-team/backintime/labels/PR%3A%20Merge%20after%20creative-break): + Merge, but with a minimum delay of one week to allow other maintainers to review. + - [PR: Waiting for review](https://github.com/bit-team/backintime/labels/PR%3A%20Waiting%20for%20review): + Wait until a second approval from another maintainer. + +The maintenance team members are promptly notified of your request. One of +them will respond within days or weeks. Note that all team members perform +their duties voluntarily in their limited spare time. +Please read the maintainers' responses carefully, answer their questions, and +try to follow their instructions. Do not hesitate to ask for clarification if +needed. At least one maintainer will review and ultimately approve your pull +request. + +Depending on the topic or impact of the PR, the maintainer may decide +that an approval from a second maintainer is needed. This may result in +additional waiting time. Please remain patient. In such cases, the PR will be +labeled +[PR: Waiting for review](https://github.com/bit-team/backintime/labels/PR%3A%20Waiting%20for%20review). + +If no second approval is necessary, the PR is labeled +[PR: Merge after creative-break](https://github.com/bit-team/backintime/labels/PR%3A%20Merge%20after%20creative-break) +and will remain open for minimum of one week. This rule allows all maintainers +the chance to review and potentially veto the pull request. + + +# Instructions about translation + +## Terminology +- The translators, as native speakers, are the maintainers of the translation + in their language and have the final decision. All following points are + strong recommendations, but not written in stone. Language maintainers are + free to overule them for good reasons. +- "Directory" or "Folder"? We prefer "Directory". In our opinion, it is a + clearly defined technical term and more precise in describing an element in + the file system. +- Translate "Back In Time"? It is the name of the application. That shouldn't + be translated at all. +- The target user group for Back In Time consists of end users without a + technical background. Write GUI strings and messages accordingly, avoiding + technical or nerdy terminology. +- Some points of the following + [General recommendations for developers](#general-recommendations-for-develoeprs) + are also relevant for translators. + +## General recommendations for developers +The following points are about creating translatable source strings. + +- Be aware that some of our translators are not skilled in Python programming. + They might don't know about GNU gettext internals and other technical + details. They only see the translatable string in the web-frontend of our + [translation platform](https://translate.codeberg.org/engage/backintime). +- Avoid escape characters in the strings. +- Give translators enough context with providing meaningful placeholder names. +- Avoid addressing users as persons with "you". Try neutral phrases instead. +- Don't "scream" by using upper case letters (e.g. `WARNING`) or an exclamation + mark (`!`). +- Please provide a screenshot when introducing new translatable strings or + modifying them. The picture will be used on the translation web-frontend to + provide translators with more context. +- [Consider Right-to-Left (RTL) and Bidiretional (BIDI) languages](#consider-right-to-left-rtl-and-bidirectional-bidi-languages). +- [Be aware of shortcut indicators and possible duplicates](#be-aware-of-shortcut-indicators-and-possible-duplicates). +- [Treat other translators work with respect](#treat-other-translators-work-with-respect). + +```python +# Avoid escape characters for string delimiters +problematic = _('Hello \'World\'') +correct = _("Hello 'World'") + +# Avoid escape characters like new lines +problematic = _('One\nTwo')` +correct = _('One') + '\n' + _('Two') # <- Separation into multiple strings is + # no problem, because the translator + # will have a screenshot. + +# Provide meaningful placeholder names +problematic = _('Can not delete {var}.') +correct = _('Can not delete {backup_path}.') + +# Avoid addressing the person with "you" +problematic = _('Do you really want to delete this backup?') +correct = _('Is it really intended to delete this backup?') +``` + +## Consider Right-to-Left (RTL) and Bidirectional (BIDI) languages + +In short: Always include punctuation marks (e.g. colons) in the strings to +translate. + +Languages such as Arabic or Hebrew are read from right to left (RTL). To +be more precise, they can have mixed reading directions (BIDI). The GUI library used +by _Back In Time_ takes this into account when arrange elements in a +window. For example, a text-input widget is left from a label +widget. This switched order is the reason why punctuation marks (e.g. colons) +in the string of a label widget need to change their direction as well. This +task can only be performed by the translator themselves, which is why +punctuation marks need to be included in the string to translate. + +## Be aware of shortcut indicators and possible duplicates + +In short: +1. Use the character `&` to indicate the letter to access a GUI element via + keyboard shortcut. +2. Be careful not to create conflicts by using the same letter multiple times + in the same GUI context. + +The _Back In Time_ GUI can be controlled via keyboard shortcuts. In the English +version, for example, the menu _Back In Time_ in the main window can be +unfolded via `Alt+T`, _Backup_ via `Alt+B`, or _Help_ via `Alt+H`. The keyboard +letters to use are indicated in the GUI with an underlined letter. The original +string in the source code uses the character `&` in front of a letter to +indicate the shortcut and produce this underline. The example above use the +source strings `Back In &Time`, `&Backup`, and `&Help`. This illustrates why it +is not appropriate to always use the first letter for shortcuts. Here in this +example, `&Back In Time` and `&Backup` would use the same letter. + +Translating `&Backup` and `&Help` into Turkish becomes `&Yedek` and `Y&ardım`, +where using the first letter only would produce conflicts again. + +That is why the translator needs to decide which letter to use. + +## Treat other translators work with respect +Sometimes it is a matter of taste or habit how do you translate +something. People are different, therefore their translation are also +different. When modifying an existing translation please consider _Comments_ +and _History_ section of that string on our translation platform. There might +be another translator who has good reason for this translation. Don't waste +other people work for no good reason. Also use the _Comments_ to document your +own reasons if you expect discussions or conflicts. + +The translation for some specific languages (e.g. +[German](https://translate.codeberg.org/projects/backintime/common/de/)) +do have rules every translator should follow. That rules can be found in a +colored box on top of the translation platform. Open an issue if you think +they should be modified. + +# Strategy Outline +This is a broad overview of the tasks or steps to enhance _Back In Time_ as a +software and as a project. The schedule is not fixed, nor is the order of +priority. + +- [Analyzing code and behavior](#analyzing-code-and-behavior) +- [Code quality & unit tests](#code-quality--unit-tests) +- [Issues](#issues) +- [Replace and remove encryption library EncFS](#replace-and-remove-encryption-library-encfs) +- [Packaging](#packaging) +- [Code hosting](#code-hosting) +- [Graphical User Interface (GUI): Redesign and Refactoring](#graphical-user-interface-gui-redesign-and-refactoring) +- [Terminal User Interface (TUI)](#terminal-user-interface-tui) +- [Tentative rough roadmap](#tentative-rough-roadmap) +- [More stuff](#more-stuff) + +## Analyzing code and behavior + +As none of the current team members were involved in the original development +of _Back In Time_, there is a lack of deep understanding of certain aspects of +the codebase and its functionality. Part of the work done in this project +involves conducting research on the code, its features, infrastructure, and +documenting the findings. + +## Code quality & unit tests + +One challenge resembles a chicken-and-egg problem: the code structure lacks +sufficient isolation, making it difficult, if not nearly impossible in some +cases, to write valuable unit tests. Heavy refactoring of the code is +necessary, but this carries a high risk of introducing new bugs. To mitigate +this risk, unit tests are essential to catch any potential bugs or unintended +changes in the behavior of _Back In Time_. Each of the problems is blocking the +solution to the other problem. + +Considering the three major types of tests (_unit_, _integration_, _system_), +the current test suite primarily consists of _system tests_. While these +_system tests_ are valuable, their purpose differs from that of _unit tests_. +Due to the lack of _unit tests_ in the test suite, the codebase +has notably low test coverage +(see [Issue #1489](https://github.com/bit-team/backintime/issues/1489)). + +The codebase does not adhere to [PEP8](https://peps.python.org/pep-0008/), +which serves as the minimum Python coding style. Utilizing linters in their +default configuration is currently not feasible. One of our objectives is to +align with PEP8 standards and meet the requirements of code linters. +See [Issue #1755](https://github.com/bit-team/backintime/issues/1755) about it. + +## Issues + +All existing issues have been triaged by the current team. +[Labels](https://github.com/bit-team/backintime/labels) are assigned to +indicate priority, along with a +[milestone](https://github.com/bit-team/backintime/milestones) indicating which +planned release will address the issue. Some of these issues persists for a +long time and involve multiple complex problems. They can be challenging to +diagnose due to various factors. Enhancing test coverage and code quality is +one aspect aimed at finding and implementing solutions for these issues. + +## Replace and remove encryption library EncFS + +Currently, _Back In Time_ uses [EncFS](https://github.com/vgough/encfs) for +encrypting backups, but it has known security vulnerabilities (see issue +[#1549](https://github.com/bit-team/backintime/issues/1549)). This requires +to remove it. A potential candidate for replacement is +[GoCryptFS](https://github.com/rfjakob/gocryptfs). +However, lack of resources hinders this effort. If no volunteers step forward, +the encryption feature will be removed, prioritizing user security and team +maintenance efforts. See +[Issue #1734](https://github.com/bit-team/backintime/issues/1734) about the +transition process and the discussion about alternatives to EncFS. + +## Packaging + +At present, _Back In Time_ utilizes a build system that relies on `make`. However, +this approach has several shortcomings and does not adhere to modern standards +in Python packaging ([PEP 621](https://peps.python.org/pep-0621), +[PEP 517](https://peps.python.org/pep-0517), +[src layout](https://packaging.python.org/en/latest/tutorials/packaging-projects), +[pyproject.toml](https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html)). +The team intends to migrate to these contemporary standards to streamline +the maintenance of _Back In Time_ ([#1575](https://github.com/bit-team/backintime/issues/1575)). + +## Code hosting + +The plan is to move to [Codeberg.org](https://codeberg.org). See also +[this FAQ entry](FAQ.md##move-project-to-alternative-code-hoster-eg-codeberg-gitlab-). + +## Graphical User Interface (GUI): Redesign and Refactoring + +Over the years, the GUI has become increasingly complex. It requires a visual +redesign as well as code refactoring. Additionally, it lacks tests. This is +an on-going tasks. + +## Terminal User Interface (TUI) + +Various people use _Back In Time_ via the terminal, for example, through an SSH +shell on a headless server. There where several ideas of creating alternatives +to the Qt based GUI: terminal user interface (TUI) or enhance the existing +command-line interface (CLI) +([#254](https://github.com/bit-team/backintime/issues/254)); a web-frontend +([#209](https://github.com/bit-team/backintime/issues/209)). All ideas are +are rejected or postponed in favor of a human readable config file format using +TOML ([#1984](https://github.com/bit-team/backintime/issues/1984)), assuming +that a TUI or WebInterface, while convenient and pleasant, would no longer be +necessary. + +## More stuff + +- Migration of the logging mechanic to Python's own `logging` module + ([#2286](https://github.com/bit-team/backintime/issues/2286). +- Re-write interprocess communication (IPC) + ([#2260](https://github.com/bit-team/backintime/issues/2260). + +## Tentative rough roadmap +This is a broad overview of upcoming developlment steps depending on each other: + +1. Implement GoCrypt as an EncFS alternative. + Related issue: [#1734](https://github.com/bit-team/backintime/issues/1734) +2. Removing EncFS. + Related issue: [#1734](https://github.com/bit-team/backintime/issues/1734) +3. Introduce new configuration management code. + Pending PR: [#1850](https://github.com/bit-team/backintime/pull/1850) +4. Implement new config file format (TOML). + Related issue: [#1984](https://github.com/bit-team/backintime/issues/1984) + +# Licensing of contributed material +Keep in mind as you contribute, that code, docs and other material submitted to +the project are considered licensed under the same terms as the rest of the +work. With a few exceptions, this is +[GNU General Public License Version 2 or later](https://spdx.org/licenses/GPL-2.0-or-later.html) +(`GPL-2.0-or-later`). This project uses [SPDX metadata](https://spdx.dev/) to +provide detailed license and copyright information in a machine-readable +format. This data is +[available online](https://api.reuse.software/info/github.com/bit-team/backintime). +or can be read from the local repository with +[REUSE tools](https://reuse.software/). + +# Quick technical guide +> [!CAUTION] +> Please remember to create a new branch before you begin any modifications. +> Baseline your feature or bug fix branch on `dev` +> (reflecting the latest development state). + +1. Fork this repository. See Microsoft GitHub's own documentation about + [how to fork](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo). + +2. Clone your own fork to your local machine and enter the directory: + + $ git clone git@github.com:YOURNAME/backintime.git + $ cd backintime + +3. Create and checkout your own feature or bugfix branch with `dev` as baseline branch: + + $ git checkout -b myfancyfeature dev + +4. Now you can add your modifications. + +5. Commit and push it to your forked repo: + + $ git commit -am 'commit message' + $ git push + +6. Test your modifications. See section [Build & Install](#build--install) and [Testing](#testing) for further details. + +7. Visit your own repository on Microsoft GitHub's website and create a Pull Request. + See Microsoft GitHub's own documentation about + [how to create a Pull Request based on your own fork](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). + +March 2026 diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 000000000..7ae1117e8 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,1607 @@ + +December 2025 + +# FAQ - Frequently Asked Questions + + + +- [General](#general) + * [Does _Back in Time_ support full system backups?](#does-back-in-time-support-full-system-backups) + * [Does _Back in Time_ support backups on cloud storage like OneDrive or Google Drive?](#does-back-in-time-support-backups-on-cloud-storage-like-onedrive-or-google-drive) + * [Where is the log file?](#where-is-the-log-file) + * [How to read log entries?](#how-to-read-log-entries) + * [How to move backups to a new hard-drive?](#how-to-move-backups-to-a-new-hard-drive) + * [How to move a large directory in the backup source without duplicating the files in the backup?](#how-to-move-a-large-directory-in-the-backup-source-without-duplicating-the-files-in-the-backup) + * [How does _Back In Time_ compare with _Timeshift_?](#how-does-back-in-time-compare-with-timeshift) + * [Additional features beside the GUI and benefits of using BIT](#additional-features-beside-the-gui-and-benefits-of-using-bit) +- [Backups (snapshots)](#backups-snapshots) + * [Backup or Snapshot?](#backup-or-snapshot) + * [Does _Back In Time_ create incremental or full backups?](#does-back-in-time-create-incremental-or-full-backups) + * [How do backups with hard-links work?](#how-do-backups-with-hard-links-work) + * [How can I check if my backups are using hard-links?](#how-can-i-check-if-my-backups-are-using-hard-links) + * [How to use checksum to find corrupt files periodically?](#how-to-use-checksum-to-find-corrupt-files-periodically) + * [What is the meaning of the leading 11 characters (e.g. "cf...p.....") in my backup logs?](#what-is-the-meaning-of-the-leading-11-characters-eg-cfp-in-my-backup-logs) + * [Backup "WITH ERRORS": [E] 'rsync' ended with exit code 23: See 'man rsync' for more details](#backup-with-errors-e-rsync-ended-with-exit-code-23-see-man-rsync-for-more-details) + * [What happens when I remove a backup?](#what-happens-when-i-remove-a-backup) + * [How can I exclude cache folders to improve backup speed and reduce storage?](#how-can-i-exclude-cache-folders-to-improve-backup-speed-and-reduce-storage) + * [How to use extended filesystem attributes (xattr) to exclude files/directories?](#how-to-use-extended-filesystem-attributes-xattr-to-exclude-filesdirectories) + * [How does Back In Time handle open or changed files during backup?](#how-does-back-in-time-handle-open-or-changed-files-during-backup) +- [Restore](#restore) + * [After Restore I have duplicates with extension ".backup.20131121"](#after-restore-i-have-duplicates-with-extension-backup20131121) + * [Back In Time doesn't find my old backups on my new Computer](#back-in-time-doesnt-find-my-old-backups-on-my-new-computer) +- [Schedule](#schedule) + * [How does the 'Repeatedly (anacron)' schedule work?](#how-does-the-repeatedly-anacron-schedule-work) + * [Will a scheduled backup run as soon as the computer is back on?](#will-a-scheduled-backup-run-as-soon-as-the-computer-is-back-on) + * [If I edit my crontab and add additional entries, will that be a problem for BIT as long as I don't touch its entries? What does it look for in the crontab to find its own entries?](#if-i-edit-my-crontab-and-add-additional-entries-will-that-be-a-problem-for-bit-as-long-as-i-dont-touch-its-entries-what-does-it-look-for-in-the-crontab-to-find-its-own-entries) + * [Can I use a systemd timer instead of cron?](#can-i-use-a-systemd-timer-instead-of-cron) +- [Problems, Errors & Solutions](#problems-errors--solutions) + * [OverflowError: Value 1702441408 out of range for UInt32](#overflowerror-value-1702441408-out-of-range-for-uint32) + * [`SettingsDialog` object has no attribute `cbCopyUnsafeLinks`](#settingsDialog-object-has-no-attribute-cbcopyunsafelinks) + * [WARNING: A backup is already running](#warning-a-backup-is-already-running) + * [_Back in Time_ does not start and shows: The application is already running! (pid: 1234567)](#back-in-time-does-not-start-and-shows-the-application-is-already-running-pid-1234567) + * [Switching to dark or light mode in the desktop environment is ignored by BIT](#switching-to-dark-or-light-mode-in-the-desktop-environment-is-ignored-by-bit) + * [Version >= 1.2.0 works very slow / Unchanged files are backed up](#version--120-works-very-slow--unchanged-files-are-backed-up) + * [What happens if I hibernate the computer while a backup is running?](#what-happens-if-i-hibernate-the-computer-while-a-backup-is-running) + * [What happens if I power down the computer while a backup is running, or if a power outage happens?](#what-happens-if-i-power-down-the-computer-while-a-backup-is-running-or-if-a-power-outage-happens) + * [What happens if there is not enough disk space for the current backup?](#what-happens-if-there-is-not-enough-disk-space-for-the-current-backup) + * [NTFS Compatibility](#ntfs-compatibility) + * [GUI does not scale on high resolution or 4k monitors](#gui-does-not-scale-on-high-resolution-or-4k-monitors) + * [Tray icon or other icons not shown correctly](#tray-icon-or-other-icons-not-shown-correctly) + * [Non-working password safe and BiT forgets passwords (keyring backend issues)](#non-working-password-safe-and-bit-forgets-passwords-keyring-backend-issues) + * [Outdated](#outdated) + * [Segmentation fault on Exit](#segmentation-fault-on-exit) + * [Incompatibility with rsync >= 3.2.4](#incompatibility-with-rsync-324-or-newer) +- [Hardware-specific Setup](#hardware-specific-setup) + * [How to use BIT with an Ugreen NAS?](#how-to-use-bit-with-an-ugreen-nas) + * [How to use QNAP QTS NAS with BIT over SSH](#how-to-use-qnap-qts-nas-with-bit-over-ssh) + * [How to use Synology DSM 5 with BIT over SSH](#how-to-use-synology-dsm-5-with-bit-over-ssh) + * [How to use Synology DSM 6 with BIT over SSH](#how-to-use-synology-dsm-6-with-bit-over-ssh) + * [Using a non-standard port](#using-a-non-standard-port) + * [How to use Synology DSM 7 with BIT over SSH](#how-to-use-synology-dsm-7-with-bit-over-ssh) + * [Using a non-standard SSH port with a Synology NAS](#using-a-non-standard-ssh-port-with-a-synology-nas) + * ["sshfs: No such file or directory" using BIT, but manually ssh with rsync works](#sshfs-no-such-file-or-directory-using-bit-but-manually-ssh-with-rsync-works) + * [Synology: use different volume for backup](#synology-use-different-volume-for-backup) + * [How to use Western Digital MyBook World Edition with BIT over ssh?](#how-to-use-western-digital-mybook-world-edition-with-bit-over-ssh) +- [Project & Contributing & more](#project--Contributing--more) + * [Why do I need to introduce myself?](#why-do-i-need-to-introduce-myself) + * [Can I contribute without using the software?](#can-i-contribute-without-using-the-software) + * [Can you assign this to me?](#can-you-assign-this-to-me) + * [Can I use @ mentions freely in issues or PRs?](#can-i-use--mentions-freely-in-issues-or-prs) + * [Can I boost my commit count?](#can-i-boost-my-commit-count) + * [Can I submit AI-generated contributions?](#can-i-submit-ai-generated-contributions) + * [Alternative installation options](#alternative-installation-options) + * [Support for specific package formats (deb, rpm, Flatpack, AppImage, Snaps, PPA, …)](#support-for-specific-package-formats-deb-rpm-flatpack-appimage-snaps-ppa-) + + [Is BIT really not supported by Canonical Ubuntu?](#is-bit-really-not-supported-by-canonical-ubuntu) + * [Move project to alternative code hoster (e.g. Codeberg, GitLab, …)](#move-project-to-alternative-code-hoster-eg-codeberg-gitlab-) + * [How to review a Pull Request](#how-to-review-a-pull-request) +- [Testing & Building](#testing--building) + * [SSH related tests are skipped](#ssh-related-tests-are-skipped) + * [Setup SSH Server to run unit tests](#setup-ssh-server-to-run-unit-tests) + + + +# General + +## Does _Back in Time_ support full system backups? + +_Back in Time_ is suited for file-based backups. + +A full system backup is neither supported nor recommended +(even though you could use _Back in Time (root)_ and include your +root folder `\`) because +- Mounted file systems (even remote locations) +- the backup needed to be done from within the running system +- Linux kernel special files (eg. /proc) must be excluded +- locked or open files (in an inconsistent state) must be handled +- backups of additional disk partitions (bootloader, EFI...) are required to be able to boot +- a restore cannot overwrite the running system (where the backups software is running) + without the risk of crashes or losing data (for that a restore must be done from a separate boot device normally) +- ... + +For full system backups look for +- a disk imaging ("cloning") solution (eg. [Clonezilla](https://clonezilla.org/)) +- file-based backup tools that are designed for this (eg. [`Timeshift`](https://github.com/linuxmint/timeshift)) + +## Does _Back in Time_ support backups on cloud storage like OneDrive or Google Drive? + +Cloud storage as backup source or target is not support because _Back in Time_ +uses `rsync` as backend for file transfer and therefore a locally mounted file system +or a `ssh` connection is required. Neither is supported by cloud storage. + +Even with native support for mounting a cloud storage, most of the time +it won't work because of limited support for 'special' file access +which is used by BiT (eg. Linux hardlinks, atime). + +Typically "locally mounted" cloud storage uses a web-based API (REST-API) +which does not support `rsync`. + +For a discussion about this topic see [Backup on OneDrive or Google Drive](https://github.com/bit-team/backintime/issues/1166). + +## Where is the log file? + +There are three distinct logs generated: + +1. The _backup log_ contains messages specific to a particular backup at a + given time. It is stored within each backup and can be accessed through + the GUI. + +2. The _restore log_ contains messages specific to a particular restore + process. It is displayed in the GUI after each restore. It is also located + in the folder `~/.local/share/backintime/` and is named `restore_.log` for + the main profile, `restore_2.log` for the second, and so forth. + +3. The _application log_ is generated using the syslog feature of the operating + system. See [How to read log entries?](#how-to-read-log-entries) for + further details. + +## How to read log entries? + +Both the _backup_ and _restore_ log files are plain text files and can be read +accordingly. Refer to [Where is the log file?](#where-is-the-log-file). +The _application_ log is generated via [syslog](https://en.wikipedia.org/wiki/Syslog) +using the identifier `backintime`. Depending on the version of _Back In time_ and the +GNU/Linux distribution used, there are three ways to get the log entries. + +1. On modern systems: + + `journalctl --identifier backintime` + +2. With an older _Back In Time_ version (1.4.2 or older): + + `journalctl --grep backintime` + +3. If the error message `journalctl: command not found` appears, directly examine the syslog files: + + `sudo grep backintime /var/log/syslog` + +## How to move backups to a new hard-drive? + +There are three different solutions: + +1. Clone the drive with ``dd`` and enlarge the partition on the new drive to + use all space. This will **destroy all data** on the destination drive! + + ```bash + sudo dd if=/dev/sdbX of=/dev/sdcX bs=4M + ``` + + where ``/dev/sdbX`` is the partition on the source drive and + ``/dev/sdcX`` is the destination drive + + Finally use ``gparted`` to resize the partition. + +1. Copy all files using ``rsync -H`` + + ```bash + rsync -avhH --info=progress2 /SOURCE /DESTINATION + ``` + +1. Copy all files using ``tar`` + + ```bash + cd /SOURCE; tar cf - * | tar -C /DESTINATION/ -xf - + ``` + +Make sure that your `/DESTINATION` contains a folder named `backintime`, which +contains all the backups. BIT expects this folder, and needs it to import +existing backups. + +## How to move a large directory in the backup source without duplicating the files in the backup? + +If you move a file/folder in the source ("include") location that is backed-up +by BIT it will treat this like a new file/folder and create a new backup file +for it (not hard-linked to the old one). With large directories this can fill +up your backup drive quite fast. + +You can avoid this by moving the file/directory in the last backup too: + +1. Create a new backup + +2. Move the original directory + +3. Manually move the same folder inside BiTs last backup in the same way you + did with the original folder + +4. Create a new backup + +5. Remove the next to last backup (the one where you moved the directory + manually) to avoid problems with permissions when you try to restore from + that backup + +## How does _Back In Time_ compare with _Timeshift_? + +Back In Time and Timeshift are both Linux application that provides back up +functionality. + +1. Similarity + - Both programs are backup tools for Linux and they create backups at a + specific time. + - For both programs, backups are taken using rsync and hard-links, while + Common files are shared between backups which saves disk space. + - Both programs support GUI and CLI + - Both programs allow you to schedule regular backups. You can also disable + scheduled backups completely and create backups manually when required + +2. Back In Time + - It is designed to protect user data including any folders or files. + - It backs up certain folders and files that you want to protect. Modified + files are transferred, while unchanged files are linked to the new + folder. You can restore certain files and folders. + - It's great for protecting your personal data + +3. TimeShift + - It is designed for system backups which allows restoring whole Linux + system to a previous state without affecting any user data. + - It backs up system files, not including any personal data unless user + explicitly configured. + - It's good for restoring your system after an update failure or + configuration change. + +## Additional features beside the GUI and benefits of using BIT + +*Back In Time* stores the user and group name which will make it possible to +restore permissions even if UID/GID changed. Additionally current user is +stored. So if the User/Group doesn't exist on the system during restore it will +restore to the old UID/GID. + +- Inhibit suspend/hibernate during backup creation +- Shutdown system after finish +- Remove & Retention policies to keep/remove old backups on reasonable rules +- Support for Plugins and user defined callback scripts + +# Backups (snapshots) + +## Backup or Snapshot? +Until _Back In Time_ version 1.6.0 the term _snapshot_ was used, instead of +_backup_. Beginning with version 1.6.0 that term was rephrased into +_backup_. The reason was to not giving the impression that _Back In Time_ does +create images of storage volumes. + +## Does _Back In Time_ create incremental or full backups? + +Back In Time does use `rsync` and its `--hard-links` feature. +Because of that each backup is technically a full backup (contains each file) +but copies only the really changed files (to save disk space) and "reuses" unchanged +files by setting a so-called "hard-link". + +In technical terms it is not an +[incremental backups](https://en.wikipedia.org/wiki/Incremental_backup). + +## How do backups with hard-links work? + +From the answer on Launchpad to the question +[_Does auto remove smart mode merge incremental backups?_](https://answers.launchpad.net/backintime/+question/123486) + +If you create a new file on a Linux filesystem (e.g. ext3) the data will have a +unique number that is called inode. The path of the file is a link to this inode +(there is a database which stores which file point to which inode). Also every +inode has a counter for how many links point to this inode. After you created a +new file the counter is 1. + +Now you make a new hardlink. The filesystem now just has to store the new path +pointing to the existing inode into the database and increase the counter of our +inode by 1. + +If you remove a file than only the link from the path to that inode is removed +and the counter is decreased by 1. If you have removed all links to that inode +so the counter is zero the filesystem knows that it can override that block next +time you save a new file. + +First time you create a new backup with BIT all files will have an inode +counter = 1. + +#### backup0 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 1 | +| fileB | 2 | 1 | +| fileC | 3 | 1 | + +Let's say you now change ``fileB``, delete ``fileC`` and have a new ``fileD``. +BIT first makes hardlinks of all files. ``rsync`` than delete all hardlinks of +files that has changed and copy the new files. + +#### backup0 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 2 | +| fileB | 2 | 1 | +| fileC | 3 | 1 | + + +#### backup1 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 2 | +| fileB | 4 | 1 | +| fileD | 5 | 1 | + +Now change ``fileB`` again and make a new backup + +#### backup0 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 3 | +| fileB | 2 | 1 | +| fileC | 3 | 1 | + +#### backup1 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 3 | +| fileB | 4 | 1 | +| fileC | 5 | 2 | + +#### backup2 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 3 | +| fileB | 6 | 1 | +| fileD | 5 | 2 | + + +Finally smart-remove is going to remove **backup0**. All that is done by +smart-remove is to ``rm -rf`` (force delete everything) the whole directory +of **backup0**. + +#### backup0 (no longer exist) +| path | inode | counter | +|:-------|--------:|----------:| +| (empty) | 1 | 2 | +| (empty) | 2 | 0 | +| (empty) | 3 | 0 | + +#### backup1 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 2 | +| fileB | 4 | 1 | +| fileD | 5 | 2 | + +#### backup2 +| path | inode | counter | +|:-------|--------:|----------:| +| fileA | 1 | 2 | +| fileB | 6 | 1 | +| fileD | 5 | 2 | + +``fileA`` is still untouched, ``fileB`` is still available in two different +versions and ``fileC`` is gone for good. The blocks on your hdd that stored the +data for inode 2 and 3 can now get overridden. + +I hope this will shed a light on the "magic" behind BIT. If it's even more +confusing don't hesitate to ask ;) + + +## How can I check if my backups are using hard-links? + +Please compare the inodes of a file that definitely didn't change between two +backups. For this open two terminals and ``cd`` into both backups directory. +``ls -lai`` will print a list where the first column is the inode which should +be equal for the same file in both backups if the file didn't change and the +backups are incremental. The third column is a counter (if the file is no +directory) on how many hard-links exist for this inode. It should be >1. So if +you took e.g. 3 backups it should be 3. + +Don't be confused on the size of each backup. If you right click on +preferences for a backup in a file manager and look for its size, it will +look like they are all full backups (not incremental). But that's not +(necessary) the case. + +To get the correct size of each backups with respect on the hard-links you +can run: + +```bash +du -chd0 /media//backintime///1/* +``` + +Compare with option `-l` to count hardlinks multiple times: + +```bash +du -chld0 /media//backintime///1/* +``` + +(``ncdu`` isn't installed by default so I won't recommend using it) + +## How to use checksum to find corrupt files periodically? + +Starting with BIT Version 1.0.28 there is a new command line option +``--checksum`` which will do the same as *Use checksum to detect changes* in +Options. It will calculate checksums for both the source and the last backups +files and will only use this checksum to decide whether a file has changed or +not. The normal mode (without checksums) is to compare modification times and sizes +of the files which is much faster to detect changed files. + +Because this takes ages, you may want to use this only on Sundays or only the +first Sunday per month. Please deactivate the schedule for your profile in +that case. Then run ``crontab -e`` + +For daily backups on 2AM and ``--checksum`` every Sunday add: + + +``` +# min hour day month dayOfWeek command +0 2 * * 1-6 nice -n 19 ionice -c2 -n7 /usr/bin/backintime --backup-job >/dev/null 2>&1 +0 2 * * Sun nice -n 19 ionice -c2 -n7 /usr/bin/backintime --checksum --backup-job >/dev/null 2>&1 +``` + +For ``--checksum`` only at first Sunday per month add: + +``` +# min hour day month dayOfWeek command +0 2 * * 1-6 nice -n 19 ionice -c2 -n7 /usr/bin/backintime --backup-job >/dev/null 2>&1 +0 2 * * Sun [ "$(date '+\%d')" -gt 7 ] && nice -n 19 ionice -c2 -n7 /usr/bin/backintime --backup-job >/dev/null 2>&1 +0 2 * * Sun [ "$(date '+\%d')" -le 7 ] && nice -n 19 ionice -c2 -n7 /usr/bin/backintime --checksum --backup-job >/dev/null 2>&1 +``` + +Press CTRL + O to save and CTRL + X to exit +(if you editor is `nano`. Maybe different depending on your default text editor). + +## What is the meaning of the leading 11 characters (e.g. "cf...p.....") in my backup logs? + +This are from `rsync` and indicating what changed and why. Please see the +section `--itemize-changes` in the +[manpage](https://download.samba.org/pub/rsync/rsync.1#opt--itemize-changes) +of `rsync`. See also some +[rephrased explanations on Stack Overflow](https://stackoverflow.com/a/36851784/4865723). + +## Backup "WITH ERRORS": [E] 'rsync' ended with exit code 23: See 'man rsync' for more details + +[BiT Version 1.4.0 (2023-09-14)](https://github.com/bit-team/backintime/releases/tag/v1.4.0) +introduced the **evaluation of `rsync` exit codes for better error recognition**: + +Before this release `rsync` exit codes were ignored and only the backup +files parsed for errors (which does not find each error, eg. dead symbolic links +logged as `symlink has no referent`). + +This "exit code 23" message may occur at the end of backup logs and BiT logs +when `rsync` was not able to transfer some (or even all) files. See +[this comment in issue 1587](https://github.com/bit-team/backintime/issues/1587#issuecomment-1856490208) +for a list all known reasons for `rsync`'s exit code 23. + +Currently you can ignore this error after checking the full backup log which +error is hidden behind "exit code 23" (and possibly fix it - eg. delete or +update dead symbolic links). + +We plan to implement an improved handling of exit code 23 in the future +(presumably by introducing warnings into the backup log). + +## What happens when I remove a backup? + +Each backup is stored in a dated subdirectory of the "full backup path" +shown in Settings. It contains a ``backup`` directory of all the files as well +as a log of the backup's creation and some other details. Removing the +backup removes this whole directory. Each backup is independent of the +others, so other backups are not affected. However, the data of identical files is +not stored redundantly by multiple backups, so removing a backup will only +recover the space used by files that are unique to that backup. + +## How can I exclude cache folders to improve backup speed and reduce storage? + +**Why exclude cache folders?** + +Cache folders typically contain temporary files that are not necessary for backups. +Excluding them can significantly improve backup speed and reduce storage usage. + +**How to exclude cache folders:** + +1. Open Back in Time. +2. Go to the **Exclude Patterns** settings: + - Click the "Exclude" tab in the configuration window. + - Click the **Add** button to create a new exclude pattern. + +3. Add the following patterns to exclude common cache directories: + ```plaintext + .var/app/**/[Cc]ache/ + .var/app/**/media_cache/ + .mozilla/firefox/**/cache/ + .config/BraveSoftware/Brave-Browser/Default/Service Worker/CacheStorage/ + ``` + +**Explanation**: + +- `/**/` matches any directory structure leading to the specified folder. +- `[Cc]ache` matches folder names with either uppercase or lowercase "Cache." + +4. Decide whether to include or exclude the folder itself: + - To exclude only the folder’s content, use `/*` at the end of the pattern: + ```plaintext + .var/app/**/[Cc]ache/* + ``` + - To exclude the folder and its contents, omit the `/*`: + ```plaintext + .var/app/**/[Cc]ache/ + ``` + +**Tips for better results:** + +- **Check Backup Logs**: + After running a backup, review the logs to identify additional folders that + may slow down the process. Example log entries for cache files: + ```plaintext + [E] Skipping file /path/to/cache/file: Too many small files. + ``` + +- **Customize Patterns**: + Adjust the patterns to suit your specific applications. For example, modify + paths for browsers or other software you use. + +- **Test Exclude Patterns**: + Test your backup after adding patterns to ensure they work as intended. + +## How to use extended filesystem attributes (xattr) to exclude files/directories? +Please see [Issue #817](https://github.com/bit-team/backintime/issues/817) for +details. + +## Are Samba shares supported? / Does Samba support hard links? +There is no short answer to that. It depends on the configuration of the Samba +server and the filesystem of the volume/harddisk it is using. + +Generally it is not recommended to use Samba shares as backup destination. Use +an SSH profile instead. + +Further reading: +- https://superuser.com/q/855946/486099 +- https://github.com/bit-team/backintime/issues/1883 + +If you encounter clear rules about configuring Samba that it works with +_Back In Time_ in a reliable way, please let us know the details. We will than +integrate it into the documentation. + +## How does _Back in Time_ handle open or changed files during backup? + +**Explanation** + +Back In Time uses rsync to copy the files and directories specified to be +backed up in the configuration. Rsync does not lock any files that are open +or being modified and therefore the backup can be copied in an inconsistent +state. Rsync only reads a file on time when it goes through it and as a result +of this only some changes are captured by rsync. This can affect files such as +logs, browser caches, databases or virtual machine images where inconsistencies +can even lead to data corruption. + +**To reduce this risk, the following approaches can be considered:** + +- **Filesystem snapshots** + If using a filesystem like btrfs and ZFS that has a snapshot function this + can be used together with Back in Time. Filesystem snapshots provide a + read-only copy of a filesystem frozen at a specific point in time, which + ensures data integrity even for open/changing files. Configure Back In Time + to backup from this filesystem's read-only snapshot. + +- **Use exclusions** + If the filesystem does not have filesystem snapshots available, one + solution could be to exclude files that are frequently open or actively + changing. The command `lsof` in GNU/Linux presents open files and the + processes that opened them as a list. Use this list as base for + configuring BIT exclusion list. + +- **Application specific handling** + For applications that opens and modifies files frequently like databases + or virtual machines, specific solutions may be needed. Use the databases + own backup function to create a consistent copy and include that in the + BIT backup. Virtual machines products typically have ability to create + snapshots of their state, that can be included in BIT. + +- **Choose when to perform backup** + Perform backup at times where less files are open, for example at night. + + + +# Restore + +## After Restore I have duplicates with extension ".backup.20131121" + +This is because *Backup files on restore* in Options was enabled. This is +the default setting to prevent overriding files on restore. + +If you don't need them any more you can delete those files. Open a terminal +and run: + +```bash +find /path/to/files -regextype posix-basic -regex ".*\.backup\.[[:digit:]]\{8\}" +``` + +Check if this correctly listed all those files you want to delete and than run: + +```bash +find /path/to/files -regextype posix-basic -regex ".*\.backup\.[[:digit:]]\{8\}" -delete +``` + +## Back In Time doesn't find my old backups on my new Computer + +Back In Time prior to version 1.1.0 had an option called +*Auto Host/User/Profile ID* (hidden under *General* > *Advanced*) which will +always use the current host- and username for the full backup path. +When (re-)installing your computer you probably chose a different host name or +username than on your old machine. With *Auto Host/User/Profile ID* activated +Back In Time now try to find your backups under the new host- and username +underneath the ``/path/to/backintime/`` path. + +The *Auto Host/User/Profile ID* option is gone in version 1.1.0 and above. +It was totally confusing and didn't add any good. + +You have three options to fix this: + +- Disable *Auto Host/User/Profile ID* and change *Host* and *User* to match + your old machine. + +- Rename the backups path + ``/path/to/backintime/OLDHOSTNAME/OLDUSERNAME/profile_id`` to match your new + host- and username. + +- Upgrade to a more recent version of Back In Time (1.1.0 or above). + The *Auto Host/User/Profile ID* option is gone and it also comes with + an assistant to restore the config from an old backup on first start. + + + +# Schedule + +## How does the 'Repeatedly (anacron)' schedule work? + +In fact *Back In Time* doesn't use anacron anymore. It was to inflexible. But that +schedule mimics anacron. + +BIT will create a crontab entry which will start ``backintime --backup-job`` +every 15min (or once an hour if the schedule is set to *weeks*). With the +``--backup-job`` command, BIT will check if the profile is supposed to be run +this time or exit immediately. For this it will read the time of the last +successful run from ``~/.local/share/backintime/anacron/ID_PROFILENAME``. +If this is older than the configured time, it will continue creating a backup. + +If the backup was successful without errors, BIT will write the current time +into ``~/.local/share/backintime/anacron/ID_PROFILENAME`` (even if *Repeatedly +(anacron)* isn't chosen). So, if there was an error, BIT will try again at +the next quarter hour. + +``backintime --backup`` will always create a new backup. No matter how many +time elapsed since last successful backup. + +## Will a scheduled backup run as soon as the computer is back on? + +Depends on which schedule you choose: + +- the schedule ``Repeatedly (anacron)`` will use an anacron-like code. So if + your computer is back on it will start the job if the given time is gone till + last backup. + +- with ``When drive get connected (udev)`` *Back In Time* will start a backup + as soon as you connect your drive ;-) + +- old fashion schedules like ``Every Day`` will use cron. This will only start a + new backup at the given time. If your computer is off, no backup will be + created. + +## If I edit my crontab and add additional entries, will that be a problem for BIT as long as I don't touch its entries? What does it look for in the crontab to find its own entries? + +You can add your own crontab entries as you like. *Back In Time* will not touch them. +It will identify its own entries by the comment line ``#Back In Time system +entry, this will be edited by the gui:`` and the following command. You should +not remove/change that line. If there are no automatic schedules defined +*Back In Time* will add an extra comment line ``#Please don't delete these two +lines, or all custom backintime entries are going to be deleted next time you +call the gui options!`` which will prevent *Back In Time* to remove user defined +schedules. + +## Can I use a systemd timer instead of cron? + +While there is no support within *Back In Time* to directly create a systemd +timer, users can create a user timer and service units. Templates are provided +below. Optionally adjust the value for `OnCalendar=` with a valid setting. See +[`man systemd.timer`](https://manpages.debian.org/testing/systemd/systemd.timer.5) +for more. + +**Timer**: +```ini +# ~/.config/systemd/user/backintime-backup-job.timer +[Unit] +Description=Start a backintime backup once daily + +[Timer] +OnCalendar=daily +AccuracySec=1m +Persistent=true + +[Install] +WantedBy=timers.target +``` + +**Service**: +```ini +# ~/.config/systemd/user/backintime-backup-job.service +[Unit] +Description=Run backintime backup generation + +[Service] +Type=oneshot +ExecStart=/usr/bin/nice -n19 /usr/bin/ionice -c2 -n7 /usr/bin/backintime backup --background +``` + +# Problems, Errors & Solutions +## OverflowError: Value 1702441408 out of range for UInt32 +The _Back In Time_ GUI crashes and this exception appears in its terminal +output. Known to happen on restoring (#2084) and removing (#2192) of backups. +Assuming it might happen also on creating backups. + +The current hypothesis the problem was introduced or happens more often since +the migration from PyQt version 5 to version 6 (BIT version `1.5.0`). + +The fix (PR #2099) was released with version `1.6.0`. +For users prior to this version, there is a tiny workaround described in that +[issue comment](https://github.com/bit-team/backintime/issues/2084#issuecomment-2787602155). + +## `SettingsDialog` object has no attribute `cbCopyUnsafeLinks` +Wenn adding a file or directory, that is in fact a symlink, to the _Include_ +Tab in the _Manage profiles_ dialog, the BIT GUI crash and give the following +error in the terminal. + +```pytb +Traceback (most recent call last): + File "/usr/share/backintime/qt/manageprofiles/tab_include.py", line 185, in btn_include_add_clicked + self._parent_dialog.cbCopyUnsafeLinks.isChecked() or + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'SettingsDialog' object has no attribute 'cbCopyUnsafeLinks' +``` + +Introduced in version `1.5.3`. Fixed in `1.6.0`. See issue +[#2279](https://github.com/bit-team/backintime/issues/2279). + +Workaround: Don't use a symlink but the linked target. + +## WARNING: A backup is already running +_Back In Time_ uses signal files like `worker.lock` to avoid starting the same backup twice. +Normally it is deleted as soon as the backup finishes. In some case something went wrong +so that _Back In Time_ was forcefully stopped without having the chance to delete +this signal file. + +Since _Back In Time_ does only start a new backup job (for the same profile) if the signal +file does not exist, such a file need to be deleted first. But before this is done manually, +it must be ensured that _Back In Time_ really is not running anymore. It can be ensured via + +```bash +ps aux | grep -i backintime +``` + +If the output shows a running instance of _Back In Time_ it must be waited until it finishes +or killed via `kill `. + +For more details see the developer documentation: [Usage of control files (locks, flocks, logs and others)](doc/maintain/4_Control_files_usage_(locks_flocks_logs_and_others).md) + +## _Back in Time_ does not start and shows: The application is already running! (pid: 1234567) +This message occurs when _Back In Time_ is either already running or did not finish regularly (e.g. due to a crash) +and wasn't able to delete its application lock file. + +Before deleting that file manually make sure no backintime process is running +via `ps aux | grep -i backintime`. +Otherwise, kill the process. After that look into the folder +`~/.local/share/backintime` for the file `app.lock.pid` and delete it. + +For more details see the developer documentation: [Usage of control files (locks, flocks, logs and others)](doc/maintain/4_Control_files_usage_(locks_flocks_logs_and_others).md) + +## Switching to dark or light mode in the desktop environment is ignored by BIT +After restart _Back In Time_ it should adapt to the desktops current used +color theme. + +It happens because Qt does not detect theme modifications out of the +box. [Workarounds are known](https://stackoverflow.com/q/75457687), but +generate a relatively large amount of code and in our opinion are not worth +the effort. + +## Version >= 1.2.0 works very slow / Unchanged files are backed up + +After updating to >= 1.2.0, BiT does a (nearly) full backup because file +permissions are handled differently. Before 1.2.0 all destination file +permissions were set to `-rw-r--r--`. In 1.2.0 rsync is executed with `--perms` +option which tells rsync to preserve the source file permission. +That's why so many files seem to be changed. + +If you don't like the new behavior, you can use "Expert Options" +-> "Paste additional options to rsync" to add the value +`--no-perms --no-group --no-owner` in that field. + +## What happens if I hibernate the computer while a backup is running? + +*Back In Time* will inhibit automatic suspend/hibernate while a backup/restore +is running. If you manually force hibernate this will freeze the current +process. It will continue as soon as you wake up the system again. + +## What happens if I power down the computer while a backup is running, or if a power outage happens? + +This will kill the current process. The new backup will stay in +``new_snapshot`` folder. Depending on which state the process was while killing +the next scheduled backup can continue the leftover ``new_snapshot`` or it will +remove it first and start a new one. + +## What happens if there is not enough disk space for the current backup? + +*Back In Time* will try to create a new backup but rsync will fail when there +is not enough space. Depending on ``Continue on errors`` setting the failed +backup will be kept and marked ``With Errors`` or it will be removed. By +default, *Back In Time* will finally remove the oldest backups until there is +more than 1 GiB free space again. + +## NTFS Compatibility +Although devices formatted with the NTFS file system can generally be used with +*Back In Time*, there are some limitations to be aware of. + +NTFS File systems do not support the following characters in filenames or directories: + +```text +< (less than) +> (greater than) +: (colon) +" (double quote) +/ (forward slash) +\ (backslash) +| (vertical bar or pipe) +? (question mark) +* (asterisk) +``` + +If *Back In Time* tries to copy files where the filename contains those +character, an "Invalid argument (22)" error message will be displayed. + +It is recommended that only devices formatted with Unix style file systems +(such as ext4) be used. + +For more information, refer to [this Microsoft page](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions). + +## GUI does not scale on high resolution or 4k monitors +The technical details are complex and many components of the operating system +are involved. BIT itself is not involved and also not responsible for +it. Several approaches might help: +- Check your desktop environment or window manager for settings regarding + scaling. +- Because BIT is using Qt for its GUI, modifying the environment variable + `QT_SCALE_FACTOR` or `QT_AUTO_SCREEN_SCALE_FACTOR`. + See [this article](https://doc.qt.io/qt-6/highdpi.html) and + [Issue #1946](https://github.com/bit-team/backintime/issues/1946) about more + details. +## Tray icon or other icons not shown correctly + +**Status: Fixed in v1.4.0** + +Missing installations of Qt-supported themes and icons can cause this effect. +_Back In Time_ may activate the wrong theme in this +case leading to some missing icons. A fix for the next release is in preparation. + +As clean solution, please check your Linux settings (Appearance, Styles, Icons) +and install all themes and icons packages for your preferred style via +your package manager. + +See issues [#1306](https://github.com/bit-team/backintime/issues/1306) +and [#1364](https://github.com/bit-team/backintime/issues/1364). + +## Non-working password safe and BiT forgets passwords (keyring backend issues) + +**Status: Fixed in v1.3.3 (mostly) and v1.4.0** + +_Back in Time_ does only support selected "known-good" backends +to set and query passwords from a user-session password safe by +using the [`keyring`](https://github.com/jaraco/keyring) library. + +Enabling a supported keyring requires manual configuration of a configuration +file until there is e.g. a settings GUI for this. + +Symptoms are DEBUG log output (with the command line argument `--debug`) of +keyring problems can be recognized by output like: + +``` +DEBUG: [common/tools.py:829 keyringSupported] No appropriate keyring found. 'keyring.backends...' can't be used with BackInTime +DEBUG: [common/tools.py:829 keyringSupported] No appropriate keyring found. 'keyring.backends.chainer' can't be used with BackInTime +``` + +To diagnose and solve this follow these steps in a terminal: + +``` +# Show default backend +python3 -c "import keyring.util.platform_; print(keyring.get_keyring().__module__)" + +# List available backends: +keyring --list-backends + +# Find out the config file folder: +python3 -c "import keyring.util.platform_; print(keyring.util.platform_.config_root())" + +# Create a config file named "keyringrc.cfg" in this folder with one of the available backends (listed above) +[backend] +default-keyring=keyring.backends.kwallet.DBusKeyring +``` + +See also issue [#1321](https://github.com/bit-team/backintime/issues/1321) + + +## Outdated +### Segmentation fault on Exit +This problem existed at least since version 1.2.1, and should hopefully be fixed +with version 1.5.0. For all affected versions, it does not impact the +functionality of _Back In Time_ or jeopardize backup integrity. It can be +safely ignored. But please report the error when encountered in version 1.5.0 +or newer. + +See also: +- [#1768](https://github.com/bit-team/backintime/pull/1768) +- [#1095](https://github.com/bit-team/backintime/issues/1095) + +### Incompatibility with rsync 3.2.4 or newer + +**Status: Fixed in v1.3.3** + +The release (`1.3.2`) and earlier versions of _Back In Time_ are incompatible +with `rsync >= 3.2.4` +([#1247](https://github.com/bit-team/backintime/issues/1247)). + +If you use `rsync >= 3.2.4` and `backintime <= 1.3.2` there is a +workaround. Add `--old-args` in +[_Expert Options_ / _Additional options to rsync_](https://backintime.readthedocs.io/en/latest/settings.html#expert-options). +Note that some GNU/Linux distributions (e.g. Manjaro) using a workaround with +environment variable `RSYNC_OLD_ARGS` in their distro-specific packages for +_Back In Time_. In that case you may not see any problems. + + +# Hardware-specific Setup + +## How to use BIT with an Ugreen NAS? +Please see [this +blogpost](https://www.ruinelli.ch/how-to-use-backintime-with-an-ugreen-nas) by +George Ruinelli @caco3. + +## How to use QNAP QTS NAS with BIT over SSH + +To use *BackInTime* over SSH with a QNAP NAS there is still some work to be done + in the terminal. + +**WARNING**: +DON'T use the changes for ``sh`` suggested in ``man backintime``. This will +damage the QNAP admin account (and even more). Changing ``sh`` for another user +doesn't make sense either because SSH only works with the QNAP admin account! + +Please test this Tutorial and give some feedback! + +1. Activate the SSH prefix: ``PATH=/opt/bin:/opt/sbin:\$PATH`` in ``Expert + Options`` + +1. Use ``admin`` (default QNAP admin) as remote user. Only this user can connect + through SSH. Also activate on the QNAP `SFTP` on the SSH settings page. + +1. Path should be something like ``/share/Public/`` + +1. Create the public/private key pair for the password-less login with the user + you use for *BackInTime* and copy the public key to the NAS. + + ```bash + ssh-keygen -t rsa + ssh-copy-id -i ~/.ssh/id_rsa.pub @ + ``` + +To fix the message about not supported ``find PATH -type f -exec`` you need to +install ``Entware-ng``. QNAPs QTS is based on Linux but some of its packages +have limited functionalities. And so do some of the necessary ones for +*BackInTime*. + +Please follow [this install instruction](https://github.com/Entware-ng/Entware-ng/wiki/Install-on-QNAP-NAS) +to install ``Entware-ng`` on your QNAP NAS. + +Because there is no web interface yet for ``Entware-ng``, you must configure it +by SSH on the NAS. + +Some Packages will be installed by default for example ``findutils``. + +Login on the NAS and updated the Database and Packages of ``Entware-ng`` with + + ```bash + ssh @ + opkg update + opkg upgrade + ``` + +Finally install the current packages of ``bash``, ``coreutils`` and ``rsync`` + + ```bash + opkg install bash coreutils rsync + ``` + +Now the error message should be gone and you should be able to take a first +backup with *BackInTime*. + +*BackInTime* changes permissions on the backup path. The owner of the +backup has read permission, other users have no access. + +This way can change with newer versions of *BackInTime* or QNAPs QTS! + +## How to use Synology DSM 5 with BIT over SSH + +**Issue** + +*BackInTime* cannot use Synology DSM 5 directly because the SSH connection to the +NAS refers to a different root file system than SFTP does. With SSH you access +the real root, with SFTP you access a fake root (`/volume1`) + +**Solution** + +Mount `/volume1/backups` to `/volume1/volume1/backups` + +**Suggestion** + +DSM 5 isn't really up to date any more and might be a security risk. It is +strongly advised to upgrade to DSM 6! Also the setup with DSM 6 is much easier! + +1. Make a new volume named ``volume1`` (should already exist, else create it) + +1. Enable User Home Service (Control Panel / User) + +1. Make a new share named ``backups`` on ``volume1`` + +1. Make a new share named ``volume1`` on ``volume1`` (It must be the same name) + +1. Make a new user named ``backup`` + +1. Give to user ``backup`` rights Read/Write to share ``backups`` and + ``volume1`` and also permission for FTP + +1. Enable SSH (Control Panel / Terminal & SNMP / Terminal) + +1. Enable SFTP (Control Panel / File Service / FTP / SFTP) + +1. Enable rsync service (Control Panel / File Service / rsync) + +1. Since DSM 5.1: Enable Backup Service (Backup & Replication / Backup Service) + (This seems not to be available/required anymore with DSM 6!) + +1. Log on as root by SSH + +1. Modify the shell of user ``backup``. Set it to ``/bin/sh`` (``vi /etc/passwd`` then + navigate to the line that begins with ``backup``, press :kbd:`I` to enter ``Insert + Mode``, replace ``/sbin/nologin`` with ``/bin/sh``, then finally save and exit by + pressing :kbd:`ESC` and type ``:wq`` followed by :kbd:`Enter`) This step might have + to be repeated after a major update of the Synology DSM! Note: This is + quite a dirty hack! It is suggested to upgrade to DSM 6 which doesn't need + this any more! + +1. Make a new directory ``/volume1/volume1/backups`` + + + ```bash + mkdir /volume1/volume1/backups + ``` + +1. Mount ``/volume1/backups`` on ``/volume1/volume1/backups`` + + ```bash + mount -o bind /volume1/backups /volume1/volume1/backups + ``` + +1. To auto-mount it make a script ``/usr/syno/etc/rc.d/S99zzMountBind.sh`` + + + ```bash + #!/bin/sh + + start() + { + /bin/mount -o bind /volume1/backups /volume1/volume1/backups + } + + stop() + { + /bin/umount /volume1/volume1/backups + } + + case "$1" in + start) start ;; + stop) stop ;; + *) ;; + esac + ``` + + Note: If the folder ``/usr/syno/etc/rc.d`` doesn't exist, check if + ``/usr/local/etc/rc.d/`` exists. If so, put it there. (After I updated to + Synology DSM 6.0beta, the first one did not exist anymore). Make sure the + execution flag of the file is checked , else it will not get run at start! To + make it executable, run: ``chmod +x /usr/local/etc/rc.d/S99zzMountBind.sh`` + +1. On the workstation on which you try to use BIT make SSH keys for user + ``backup``, send the public key to the NAS + + ```bash + ssh-keygen -t rsa -f ~/.ssh/backup_id_rsa + ssh-add ~/.ssh/backup_id_rsa + ssh-copy-id -i ~/.ssh/backup_id_rsa.pub backup@ + ssh backup@ + ``` + +1. You might get the following error: + + ```bash + /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed + /usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system. + ``` + +1. If so, copy the public key manually to the NAS as root with + + ```bash + scp ~/.ssh/id_rsa.pub backup@:/var/services/homes/backup/ + ssh backup@ cat /var/services/homes/backup/id_rsa.pub >> /var/services/homes/backup/.ssh/authorized_keys + # you'll still be asked for your password on these both commands + # after this you should be able to login password-less + ``` + +1. And proceed with the next step + +1. If you are still prompted for your password when running ``ssh + backup@``, check the permissions of the file + ``/var/services/homes/backup/.ssh/authorized_keys``. It should be + ``-rw-------``. If this is not the case, run the command + + ```bash + ssh backup@ chmod 600 /var/services/homes/backup/.ssh/authorized_keys + ``` + +1. Now you can use *BackInTime* to perform your backup to your NAS with the user + ``backup``. + +## How to use Synology DSM 6 with BIT over SSH + +1. Enable User Home Service (Control Panel / User / Advanced). There is no need + to create a volume since everything is stored in the home directory. + +1. Make a new user named ``backup`` (or use your existing account). Add this user + to the user group ``Administrators``. Without this, you will not be able to log + in! + +1. Enable SSH (Control Panel / Terminal & SNMP / Terminal) + +1. Enable SFTP (Control Panel / File Service / FTP / SFTP) + +1. Since DSM 5.1: Enable Backup Service (Backup & Replication / Backup Service) + (This seems not to be available/required anymore with DSM 6!) (Tests needed!) + +1. On DSM 6 you can edit the user-root-dir for sFTP: Control Panel -> File + Services -> FTP -> General -> Advanced Settings -> Security Settings -> + Change user root directories -> Select User. Now select the user ``backup`` and + Change root directory to ``User home`` + +1. On the workstation on which you try to use BIT make SSH keys for user + ``backup``, send the public key to the NAS + + ```bash + ssh-keygen -t rsa -f ~/.ssh/backup_id_rsa + ssh-add ~/.ssh/backup_id_rsa + ssh-copy-id -i ~/.ssh/backup_id_rsa.pub backup@ + ssh backup@ + ``` + +1. You might get the following error: + + ```bash + /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed + /usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system. + ``` + +1. If so, copy the public key manually to the NAS as root with + + ```bash + scp ~/.ssh/id_rsa.pub backup@:/var/services/homes/backup/ + ssh backup@ cat /var/services/homes/backup/id_rsa.pub >> /var/services/homes/backup/.ssh/authorized_keys + # you'll still be asked for your password on these both commands + # after this you should be able to login password-less + ``` + +1. And proceed with the next step + +1. If you are still prompted for your password when running ``ssh + backup@``, check the permissions of the file + ``/var/services/homes/backup/.ssh/authorized_keys``. It should be + ``-rw-------``. If this is not the case, run the command + + ```bash + ssh backup@ chmod 600 /var/services/homes/backup/.ssh/authorized_keys + ``` + +1. In *BackInTime* settings dialog leave the *Path* field empty + +1. Now you can use *BackInTime* to perform your backup to your NAS with the user + ``backup``. + +### Using a non-standard port + +If you want to use the Synology NAS with non-standard SSH/SFTP port +(standard is 22), you have to change the Port on total 3 places: + +1. Control Panel > Terminal: Port = + +1. Control Panel > FTP > SFTP: Port = + +1. Backup & Replication > Backup Services > Network Backup Destination: SSH + encryption port = + +Only if all 3 of them are set to the same port, *BackInTime* is able to +establish the connection. As a test, one can run the command + + ```bash + rsync -rtDHh --checksum --links --no-p --no-g --no-o --info=progress2 --no-i-r --rsh="ssh -p -o IdentityFile=/home//.ssh/id_rsa" --dry-run --chmod=Du+wx /tmp/ "@:/volume1/Backups/BackinTime" + ``` + +in a terminal (on the client PC). + +## How to use Synology DSM 7 with BIT over SSH + +1. Enable *User Home Service* (Control Panel > User & Group > Advanced). + +1. Make a new user named ``backup`` (or use your existing account) and add this +user to the user group ``Administrators``. + +1. Enable *SSH* (Control Panel > Terminal & SNMP > Terminal) + +1. Enable *SFTP* (Control Panel > File Services > FTP > SFTP) + +1. Enable *rsync* (Control Panel > File Services > rsync) + +1. Edit the user-root-directory for SFTP: Control Panel > File Services > FTP > +General > Advanced Settings > Security Settings > Change user root directories > +Select User > select the user ``backup`` > Edit and Change root directory to ``User +home`` + +1. Make sure the 'homes' shared folder has the default permissions and that + non-admin users and groups are not assigned Read or Write permissions on the + 'homes' folder. The default permissions are described in [this guide](https://kb.synology.com/DSM/tutorial/default_permissions_of_homes) + +1. On the workstation on which you need to use BIT, make an SSH key pair for + user ``backup``, and send the public key to the NAS: + + ```bash + ssh-keygen -t rsa -f ~/.ssh/backup_id_rsa + ssh-copy-id -i ~/.ssh/backup_id_rsa.pub backup@ + ssh backup@ + ``` + +1. Although not strictly necessary, Synology recommend setting the permissions + for the `.ssh` directory and the `authorized_keys` file to `700`, and `600` + respectively: + + ```bash + backup@NAS:~$ chmod 700 .ssh + backup@NAS:~$ chmod 600 .ssh/authorized_keys + ``` +1. In *BackInTime* settings dialog leave the *Path* field empty + +1. Now you can use *BackInTime* to perform your backup to your NAS with the user +``backup``. + +### Using a non-standard SSH port with a Synology NAS + +If you want to use the Synology NAS with a non-standard SSH/SFTP port as advised +by the Security Advisor package, you have to change the Port in 3 places (the +default port number for all three is 22): + +1. Control Panel > Terminal & SNMP > Terminal: Port = + +1. Control Panel > File Services > FTP > SFTP: Port number = + +1. Control Panel > File Services > rsync > SSH encryption port = + +Only if all 3 are set to the same port is *BackInTime* able to establish the +connection (don't forget to set the new port number in the BIT profiles). + +To sign in with ssh using the new port number: + + ```bash + ssh -p PORT_NUMBER backup@ + ``` + +or, for convenience you can edit or create ``~/.ssh/config`` with the following: + + ``` + Host + Port PORT_NUMBER + ``` + +and then use just: + + ```bash + ssh backup@ + ``` + +### "sshfs: No such file or directory" using BIT, but manually ssh with rsync works +The reason (known for DSM version 7) is that the setup of ssh and sftp is +customized by Synology. + +Solution ([Screenshot in Issue #1674](https://github.com/bit-team/backintime/issues/1674#issuecomment-2106059151)): +1. Go to: _Control Panel_ > _File Services_ > _Advanced Settings_ > _Change user root directories_ > _Select User_ +2. Add the name of the user used for SSH on the Synology in that list. +3. At _Change root directory to:_ select _User home_. + +See also +- [Issue #1674](https://github.com/bit-team/backintime/issues/1674) +- ["Change the default folder in a Synology NAS" - StackOverflow](https://stackoverflow.com/a/77454561/4865723) + +## Synology: use different volume for backup + +This was tested and related to Synology DSM version 7, but might work with +other versions, too. Feel free to report back. + +If you want to use a different volume as the destination for the backup use +these additional steps: + +1. Follow all steps under **Howto (like create additional user in the example + name of the user 'backup') +2. Create in the Synology DSM GUI in Control panel a new shared folder name it + "backup" for example + + ![Synology DSM7 Basic Setup](doc/images.misc/faq_synology7_separate_dest_volume01.png) + +3. Optional in step-2 Enable shared folder encryption (Depending on your needs, + don't loose your encryption key) Advantage: backup folder (volume) is + encrypted, even in case of theft of your Synology NAs Disadvantage: On each + Reboot you need to mount the folder manually + + ![Synology DSM7 Additional Security Measure](doc/images.misc/faq_synology7_separate_dest_volume02.png) + +4. As user root or with sudo edit the file: `/etc/passwd` (Be careful, if you + break it, you could break your NAS) + - `vi /etc/passwd` + - Edit the line for your user backup, so the home dir is on the newly + created folder: `backup:x:1038:100:Back in Time User:/volume1/backup:/bin/sh` +5. Continue with your normal setup of BIT + +## How to use Western Digital MyBook World Edition with BIT over ssh? + +Device: *WesternDigital MyBook World Edition (white light) version 01.02.14 (WD MBWE)* + +The BusyBox that is used by WD in MBWE for serving basic commands like ``cp`` +(copy) doesn't support hardlinks. Which is a rudimentary function for +BackInTime's way of creating incremental backups. As a work-around you can +install Optware on the MBWE. + +Before proceeding please make a backup of your MBWE. There is a significant +chance to break your device and lose all your data. There is good +documentation about Optware on http://mybookworld.wikidot.com/optware. + +1. You have to login to MBWE's web admin and change to *Advanced Mode*. + Under *System | Advanced* you have to enable *SSH Access*. Now you can + log in as root over ssh and install Optware (assuming ```` is the + address of your MyBook). + + Type in terminal: + + ```bash + ssh root@ #enter 'welc0me' for password (you should change this by typing 'passwd') + wget http://mybookworld.wikidot.com/local--files/optware/setup-whitelight.sh + sh setup-whitelight.sh + echo 'export PATH=$PATH:/opt/bin:/opt/sbin' >> /root/.bashrc + echo 'export PATH=/opt/bin:/opt/sbin:$PATH' >> /etc/profile + echo 'PermitUserEnvironment yes' >> /etc/sshd_config + /etc/init.d/S50sshd restart + /opt/bin/ipkg install bash coreutils rsync nano + exit + ``` + +1. Back in MBWE's web admin go to *Users* and add a new user (```` + in this How-to) with *Create User Private Share* set to *Yes*. + + In terminal: + + ```bash + ssh root@ + chown /shares/ + chmod 700 /shares/ + /opt/bin/nano /etc/passwd + #change the line + #:x:503:1000:Linux User,,,:/shares:/bin/sh + #to + #:x:503:1000:Linux User,,,:/shares/:/opt/bin/bash + #save and exit by press CTRL+O and CTRL+X + exit + ``` + +1. Next create the ssh-key for your local user. + In the terminal + + ```bash + ssh @ + mkdir .ssh + chmod 700 .ssh + echo 'PATH=/opt/bin:/opt/sbin:/usr/bin:/bin:/usr/sbin:/sbin' >> .ssh/environment + exit + ssh-keygen -t rsa #enter for default path + ssh-add ~/.ssh/id_rsa + scp ~/.ssh/id_rsa.pub @:./ #enter password from above + ssh @ #you will still have to enter your password + cat id_rsa.pub >> .ssh/authorized_keys + rm id_rsa.pub + chmod 600 .ssh/* + exit + ssh @ #this time you shouldn't been asked for password anymore + exit + ``` + +1. You can test if everything is done by enter this + + ```bash + ssh @ cp --help + ``` + + The output should look like: + + ```bash + Usage: cp [OPTION]... [-T] SOURCE DEST + or: cp [OPTION]... SOURCE... DIRECTORY + or: cp [OPTION]... -t DIRECTORY SOURCE... + Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY. + + Mandatory arguments to long options are mandatory for short options too. + -a, --archive same as -dR --preserve=all + --backup[=CONTROL] make a backup of each existing destination file + -b like --backup but does not accept an argument + --copy-contents copy contents of special files when recursive + ... (lot more lines with options) + ``` + + But if your output looks like below you are still using the BusyBox and + will not be able to run backups with *BackInTime* over ssh: + + ```bash + BusyBox v1.1.1 (2009.12.24-08:39+0000) multi-call binary + + Usage: cp [OPTION]... SOURCE DEST + ``` + + +# Project & Contributing & more + +## Why do I need to introduce myself? +It helps maintainers understand who you are and how to communicate with +you. This saves unnecessary effort on both sides by avoiding misunderstandings +that could lead to rejected contributions. It also helps distinguish genuine +contributors from accounts submitting low-quality or AI-generated changes +merely to inflate commit statistics or stars, without real engagement in the +project. + +Here is a small suggestion and guidance for your introduction: + +- How long and in what way have you been using BIT? +- What experience and skills do you have in software development? +- What are your current learning goals? +- How did you become aware of this issue? + +## Can I contribute without using the software? +No, in most cases. Contributors must be users of _Back In Time_. Real +contributions require familiarity with the software, its behavior, and +workflows. Real contributions come from real usage. + +## Can you assign this to me? +No. Don't ask. Comment with intent or a plan first. Otherwise its just noise. +Your behavior disrespects contributors with real intent, and burden maintainers +who work on this project in their free time. Don't waste our time. + +## Can I use @ mentions freely in issues or PRs? +No. Never. Avoid them in all cases. Mentions trigger notifications and create +noise. Maintainers and subscribed contributors already see all activity. + +## Can I boost my commit count? +No. Doing that can get your account blocked or deleted, because mainters will +report you to the abuse team of Microsoft. This project isn't for collecting +stars or commits. Maybe watching +[Don't Contribute to Open Source](https://www.youtube.com/watch?v=5nY_cy8zcO4) +will help you to understand and learn. + +## Can I submit AI-generated contributions? +No. AI-generated contributions are prohibited. Attempting this will be +reported to Microsoft abuse team, and your account may be blocked or deleted. + +## Alternative installation options +Besides the repositories of the official GNU/Linux distributions, there are +other alternative installation options provided and maintained by third +parties. Use them at your own risk and please contact that third party +maintainers if you encounter problems. **Again**: We strongly recommend not to +use 3rd party repositories because of possible security issues. + +- [@jean-christophe-manciot](https://github.com/jean-christophe-manciot)'s PPA distributing [_Back In Time_ for the latest stable Ubuntu release](https://git.sdxlive.com/PPA/about). See [PPA requirements](https://git.sdxlive.com/PPA/about/#requirements) and [install instructions](https://git.sdxlive.com/PPA/about/#installing-the-ppa). +- The Arch User Repository ([AUR](https://aur.archlinux.org/)) does offer [some packages](https://aur.archlinux.org/packages?K=backintime). + + +## Support for specific package formats (deb, rpm, Flatpack, AppImage, Snaps, PPA, …) + +We assist and support other projects providing specific distribution +packages. Thus, we suggest creating your own repository to manage and maintain +such packages. It will be mentioned in our documentation as an alternative +source for installation. + +We do not directly support third-party distribution channels associated with +specific GNU/Linux distributions, unofficial repositories (e.g. Arch AUR, +Launchpad PPA) or FlatPack & Co. One reasons is our lack of resources and the +need to prioritize tasks. Another reasons is that their are distro maintainers +with much more experience and skills in packaging. We always recommend using +the official repositories of GNU/Linux distributions and contacting their +maintainers if _Back In Time_ is unavailable or out dated. + +## Is BIT really not supported by Canonical Ubuntu? + +Ubuntu consists of +[several repositories](https://help.ubuntu.com/community/Repositories), each +offering different levels of support. The `main` repository is maintained +by Canonical and receives regular security updates and bug fixes throughout +the 5-year support period of LTS releases. + +In contrast, the `universe` repository is community-managed, meaning security +updates and bug fixes are not guaranteed and depend heavily on community +activity and volunteers. Therefore, packages in `universe` may not always be +up-to-date with the same but well-maintained packages in Debian GNU/Linux and +might miss important fixes. + +_Back In Time_ is one such package in the `universe` repository. That +[package](https://packages.ubuntu.com/search?suite=all&searchon=names&keywords=backintime) +is copied from the +[Debian GNU/Linux repository](https://packages.debian.org/search?searchon=sourcenames&keywords=backintime). +It can be said that _Back In Time_ is not maintained by Canonical Ubuntu, but +by volunteers from the Community of Ubuntu. + +## Move project to alternative code hoster (e.g. Codeberg, GitLab, …) + +We also believe that staying with Microsoft GitHub is not a good idea. Microsoft +GitHub does not offer any exclusive feature for our project that another hoster +could not also provide. But a migration is a matter of time and resources we +currently do not have. But it is on our list. And with the current state of +discussion we seem to target [Codeberg.org](https://codeberg.org). + +For more details please see +[this thread on the mailing list](https://mail.python.org/archives/list/bit-dev@python.org/message/O5XZ5SPW6WIFBFKWUBHSOUIBKEUIBPNM/). + + +## How to review a Pull Request +Reviewing a Pull Request (PR) isn’t just about the code—it’s also about +functionality. Changes can be tested by installing _Back In Time_ and trying +them out, even without reading the code. This allows issues to be identified +from a user’s perspective. A second pair of eyes helps catch errors, spot +overlooked issues, and improve overall quality. Fresh perspectives, knowledge +sharing, and better maintainability contribute to the long-term stability of +the project. + +Check PRs labeled with +[PR: Waiting for +review](https://github.com/bit-team/backintime/pulls?q=is%3Aopen+is%3Apr+label%3A%22PR%3A+Waiting+for+review%22). +Checking the [milestone](https://github.com/bit-team/backintime/milestones) +assigned to PR can also help gauge their priority and urgency. + +- Start by carefully reading the PR description to understand the proposed + changes. Ask back if something is not clear. +- When giving feedback, consider the contributor’s level of experience and + skills. Keep it polite and constructive—every beginner could be a future + maintainer. + +To **test functionality**, +[check out the PR code locally](https://docs.github.com//pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally) +on a virtual machine or your local machine. Running _Back In Time_ in a test +environment provides insights, that can be shared as findings, observations, +or suggestions for improvement. + +About **code review**: +- Code should follow + [project standards](CONTRIBUTING.md#best-practice-and-recommendations) + and be structured for long-term maintainability. +- Is a PR too large or complex, suggest to breaking it down into smaller parts. +- How is the documentation? +- Are there unit tests? +- Does the changelog need an entry? + +# Testing & Building + +## SSH related tests are skipped + +They get skipped if no SSH server is available. Please see section +[Testing & Building](CONTRIBUTING.md#testing--building) about how to setup +a SSH server on your system. + +## Setup SSH Server to run unit tests + +Please see section [Testing - SSH](CONTRIBUTING.md#ssh). diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 000000000..c3e2096e9 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,167 @@ + +# Looking back in time at _Back In Time_ + +*by Michael Büker, 2024* + +The development of _Back In Time_ was inspired by +[FlyBack](https://en.wikipedia.org/wiki/FlyBack). +The history of the _Back In Time_ project, which at the time of this writing +already spans nearly 16 years and is best understood in four periods: + +1. The **First Era** from 2008 to 2012, releases 0.5 to ~1.0.12 +2. The **Second Era** from 2012 to 2019, releases ~1.0.14 to 1.2 +3. A **Dark Age** from 2019 to 2022, releases 1.2.0 to 1.3.2 +4. The **Third Era** since 2022, since release 1.3.3 + +These periods correspond roughly to who was maintaining and developing _Back In +Time_. Important technical and organizational changes happened at various +moments in between. + +For details, refer to [CHANGES](CHANGES). For a glimpse into the future, see +the [Strategy Outline](CONTRIBUTING.md#strategy-outline). + +# The First Era: 0.5 to ~1.0.12 (2008–2012) + +## Maintenance + +_Back In Time_ was created by **Oprea Dan** and first published on a private +blog in late 2008 +([wayback link](https://web.archive.org/web/20081014041759/http://www.le-web.org/2008/10/03/back-in-time-version-05/)). +Shortly thereafter, collaborative development started happening on +Launchpad. Sometime around 2010, development and publication appears to have +moved entirely to Launchpad, with the private blog being discontinued. + +## Core functionality + +At first, _Back In Time_ used `diff` to compare the latest backup with the +source, in order to check if a new backup was necessary. If the answer was +yes, it would use `cp` to create a new backup. + +This was changed in version 0.9.2 in early 2009, when `diff` was replaced by +`rsync` for the comparison. Copying was still done by `cp`, apparently without +special permissions handling. + +This changed when, shortly thereafter, version 0.9.24 introduced +`fileinfo.bz2`, which holds permissions information on all files in a +backup. Introduced to allow saving backups on non-Unix-permission-aware +filesystems like NTFS, `fileinfo.bz2` is consulted upon restoring a file in +order to recreate its original ownership and permissions. + +## GUI + +Initially, _Back In Time_ had only a GNOME GUI. + +Version 0.9 from early 2009 separated the backend (`backintime-common`) from +the GUI, allowing for different frontends. Over the course of 2009, finishing +roughly with version 0.9.24, two separate frontends were completed: +`backintime-gnome` and `backintime-kde4`. + +# The Second Era: ~1.0.14 to 1.2 (2012–2019) + +## Maintenance + +Around 2012, **Germar Reitze** took over publication, maintenance and further +development from Oprea Dan. + +In early 2016, starting with version 1.1.10, development and publication moved +to Microsoft GitHub, leaving the Launchpad project mostly abandoned (except for +translation management and PPA publication). + +## Core functionality +Development during the Second Era centered largely around remote backup +capabilities. + +In late 2012, version 1.0.12 introduced remote backup locations enabled by +`ssh`. + +In early 2013, version 1.0.22 introduced an optional "full rsync mode". This +replaced `cp` with `rsync` for all operations, including full replication of +permissions. + +In late 2013, version 1.0.26 introduced encrypted backup locations enabled by +`encfs`. + +## GUI + +In early 2015, version 1.1.0 eliminated the separate `backintime-gnome` and +`backintime-kde4` frontends and introduced `backintime-qt4` as the only +frontend. + +# The Dark Age: 1.2.0 to 1.3.2 (2019–2022) +In 2019, version 1.2.0 was released. It was the first release since version +1.1.24 in late 2017 and contained many bugfixes accumulated over the previous +1.5 years. + +Version 1.2.0 introduced a fundamental change: ***"make full-rsync mode +default, remove the other mode"***. This meant that files would always be +transferred by `rsync` instead of `cp`. Specifically, `rsync` was instructed to +retain full ownership and permissions information when transferring the files +to the backup (*in addition* to the information stored in `fileinfo.bz2`). + +This caused bug [#988](https://github.com/bit-team/backintime/issues/988), +which broke _Back In Time_'s core functionality for any backup created with +version <1.2.0 (unless "full rsync mode" had been enabled): many unchanged +files were no longer hardlinked upon transferring, but unnecessarily +copied. This led to very long backup times and high disk usage. A related bug +with a somewhat smaller impact is +[#994](https://github.com/bit-team/backintime/issues/994). + +As these bugs are currently understood, the underlying reason for the problem +is differing ownership/permissions between the files in the source and on the +backup drive. Since multiple hardlinks to the same file are, by definition, +identical, they cannot have differing permissions. `rsync` fails to handle this +case correctly when a new backup is created, leading to the files in question +being copied unnecessarily. + +With many users complaining and trading workarounds on Microsoft GitHub, +development soon came to a halt. Some bugs were fixed with version 1.3.0 in +2021, but [#988](https://github.com/bit-team/backintime/issues/988) and +[#994](https://github.com/bit-team/backintime/issues/994) remained. + +# The Third Era: since 1.3.3 (since 2022) + +In early 2022, an epic discussion on the state of the project arose in +[#1232](https://github.com/bit-team/backintime/issues/1232). Many users +declared their love for _Back In Time_, and a few were ready to step up and +restart development. With help and permission from Germar Reitze, **Christian +Buhtz**, **Jürgen Altfeld** and **Michael Büker** formed a new core team. The +team first curated and triaged over 200 open issues that had accumulated since +2019. + +The first release by the new team was version 1.3.3 in early 2023. Early work +focused on ensuring compatibility with rsync 3.2.4, fixing keyring issues for +SSH operations, system tray functionality in both X11 and Wayland as well as +testing, coding style and other modernization to align _Back In Time_ with +current Python practices. + +## Core functionality + +Work on fixing [#988](https://github.com/bit-team/backintime/issues/988) and +[#994](https://github.com/bit-team/backintime/issues/994) is still ongoing as +of this writing. These bugs are largely understood now, but any possible fix +could potentially have grave consequences for existing backups, which have not +been thoroughly tested for. + +Given that EncFS suffers from known security issues and is not actively +maintained, _Back In Time_ is preparing to deprecate it in the foreseeable +future ([#1734](https://github.com/bit-team/backintime/issues/1734)). + +## GUI + +The GUI is slated for a redesign and code refactoring, as it has become complex +and convoluted over the years. + +A commonly requested feature is a terminal user interface (TUI), or an +enhancement of the existing command-line interface (CLI), as discussed in +[#254](https://github.com/bit-team/backintime/issues/254). The proposal for a +web frontend was rejected +([#209](https://github.com/bit-team/backintime/issues/209)), but separate +projects offering a web fronted would be supported. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d511905c1..000000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/LICENSES.md b/LICENSES.md new file mode 100644 index 000000000..2b6f27e9b --- /dev/null +++ b/LICENSES.md @@ -0,0 +1,20 @@ + +The directory [`LICENSES`](LICENSES) contains all relevant license files +regarding the _Back In Time_ project. The projects primary license is +[GNU General Public License v2.0 or later (`GPL-2.0-or-later`)](https://spdx.org/licenses/GPL-2.0-or-later.html). + +Copyright and license information are available for each file via +[SPDX metadata](https://spdx.dev) following +[REUSE.software standard](https://reuse.software). +Read the header of each file and the `REUSE.toml` files, or use the `reuse` +tool to extract the information. + +Refer to [`README.md`](README.md) for further information about the project. diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/GPL-2.0-or-later.txt b/LICENSES/GPL-2.0-or-later.txt new file mode 100644 index 000000000..17cb28643 --- /dev/null +++ b/LICENSES/GPL-2.0-or-later.txt @@ -0,0 +1,117 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice diff --git a/LICENSES/GPL-3.0-or-later.txt b/LICENSES/GPL-3.0-or-later.txt new file mode 100644 index 000000000..f6cdd22a6 --- /dev/null +++ b/LICENSES/GPL-3.0-or-later.txt @@ -0,0 +1,232 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 000000000..2071b23b0 --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 5b3d6a58b..e53c827a1 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,147 @@ -# Back In Time - -Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze, Taylor Raack - -[![Build Status](https://travis-ci.org/bit-team/backintime.svg?branch=master)](https://travis-ci.org/bit-team/backintime) -[![Coverage Status](https://coveralls.io/repos/github/bit-team/backintime/badge.svg?branch=master)](https://coveralls.io/github/bit-team/backintime?branch=master) -[![Documentation Status](https://readthedocs.org/projects/backintime-dev/badge/?version=latest)](http://backintime.readthedocs.org/projects/backintime-dev/en/latest/?badge=latest) - -## About - -Back In Time is a simple backup tool for Linux, inspired by "flyback project". - -It provides a command line client 'backintime' and a Qt5 GUI 'backintime-qt' -both written in Python3. - -You only need to specify 3 things: -* where to save snapshots -* what folders to backup -* backup frequency (manual, every hour, every day, every month) - -## Documentation + +[![Mailing list bit-dev@python.org](doc/maintain/_images/badge_bit-dev.svg)](https://mail.python.org/mailman3/lists/bit-dev.python.org/) +[![Mastodon @backintime@fosstodon.org](doc/maintain/_images/badge_mastodon.svg)](https://fosstodon.org/@backintime) -Please ask questions and report bug on -https://github.com/bit-team/backintime/issues +[![Build Status](https://app.travis-ci.com/bit-team/backintime.svg?branch=dev)](https://app.travis-ci.com/bit-team/backintime) +[![User manual Status](https://readthedocs.org/projects/backintime/badge/?version=latest)](https://backintime.readthedocs.io) +[![Translation status](https://translate.codeberg.org/widget/backintime/common/svg-badge.svg)](https://translate.codeberg.org/engage/backintime) +[![REUSE status](https://api.reuse.software/badge/github.com/bit-team/backintime)](https://api.reuse.software/info/github.com/bit-team/backintime) -## Download - -Please find the latest versions on -https://github.com/bit-team/backintime/releases/latest - -## INSTALL - -Back In Time is included in many distributions and can be installed from their -repositories. - -##### Ubuntu PPA - -We provide a PPA (Private Package Archive) with current stable version -(ppa:bit-team/stable) and a testing PPA (ppa:bit-team/testing) - - sudo add-apt-repository ppa:bit-team/stable - sudo apt-get update - sudo apt-get install backintime-qt4 - -or +# Back In Time - sudo add-apt-repository ppa:bit-team/testing - sudo apt-get update - sudo apt-get install backintime-qt +_Back In Time_ is a comfortable and well-configurable graphical frontend for +incremental backups using [`rsync`](https://rsync.samba.org/), with a +command-line version also available. Modified files are transferred, while +unchanged files are linked to the new directory using rsync's hard link feature, +saving storage space. Restoring is straightforward via file manager, command +line or _Back In Time_ itself. + +It is written in Python3 and available for all major GNU/Linux distributions +as command line tool `backintime` and GUI `backintime-qt`. Backups can be +scheduled and stored locally or remotely through SSH. + +More background info in [CONTRIBUTING](CONTRIBUTING.md) and +[HISTORY](HISTORY.md). + +## Maintenance status + +The project is in active development since the [new team](#the-team) joined in +summer 2022. Development is done voluntarily in spare time so things need to be +prioritized. Stick with us, we all ♥️ _Back In Time_. 😁 -##### Debian/Ubuntu make packages +Current focus is on fixing +[major issues](https://github.com/bit-team/backintime/issues?q=is%3Aissue+is%3Aopen+label%3AHigh) +instead of implementing new +[features](https://github.com/bit-team/backintime/labels/Feature). +Stabilize the code base and its test suite is also a matter. Read the +[strategy outline](CONTRIBUTING.md#strategy-outline) for details. +Please see [CONTRIBUTING](CONTRIBUTING.md) if you are interested in the +development and have a look on +[open issues](https://github.com/bit-team/backintime/issues) especially +those labeled as [good first issues](https://github.com/bit-team/backintime/labels/GOOD%20FIRST%20ISSUE) +and [help wanted](https://github.com/bit-team/backintime/issues?q=is%3Aissue+is%3Aopen+label%3AHELP-WANTED). - ./makedeb.sh - sudo dpkg -i ../backintime-common-.deb - sudo dpkg -i ../backintime-qt-.deb +## The team +Since around 2024, [@buhtz](https://buhtz.codeberg.page/), part of the projects +third generation of maintainers, has been the sole maintainer. He handles all +core tasks, from code analysis and documentation to issue resolution and +feature implementation. The work is carried out voluntarily during spare +time. The project continues to benefit from an active and engaged community +that provides advice, expertise, and contributions, ensuring it thrives and +evolves. -##### ArchLinux +The project was +[reactivated in 2022](https://github.com/bit-team/backintime/issues/1232)) +and thanks in large part to @emtiu and @aryoda, who helped relaunch and +shape its direction. See [HISTORY](HISTORY.md) for more details. -Back In Time is available through AUR. You need to import a public key once -before installing +# Index - gpg --keyserver pgp.mit.edu --recv-keys 615F366D944B4826 - # Fingerprint: 3E70 692E E3DB 8BDD A599 1C90 615F 366D 944B 4826 - wget https://aur.archlinux.org/cgit/aur.git/snapshot/backintime.tar.gz - tar xvzf backintime.tar.gz - cd backintime - makepkg -srci +- [Documentation](#documentation) +- [Contact & Social](#contact--social) +- [Installation](#installation) +- [Known Problems and Workarounds](#known-problems-and-workarounds) +- [Contributing and other ways to support the project](#contributing-and-other-ways-to-support-the-project) +- [Licenses](#licenses) -### From sources +--- -##### Common +# Documentation -* dependencies - - python3 (>= 3.3) - - rsync - - cron-daemon - - openssh-client - - python3-keyring - - python3-dbus + * [FAQ - Frequently Asked Questions](FAQ.md) + * [End user documentation](https://backintime.readthedocs.org/) (not totally up-to-date) + * [Source code documentation for developers](https://backintime-dev.readthedocs.org) + (**Disabled** and not up-2-tdate. Please open an issue if you need to use it.) -* recomended - - sshfs - - encfs +# Contact & Social -* Command + * **Mailing list**: + [bit-dev@python.org](https://mail.python.org/mailman3/lists/bit-dev.python.org/) + can be used for **any topic**, question and idea related to _Back In + Time_. Despite its name it is not restricted to development topics only. + * **Fediverse** on **Mastodon**: [@backintime@fosstodon.org](https://fosstodon.org/@backintime) + * **Bugs** & **Feature Requests**: [Issues section](https://github.com/bit-team/backintime/issues) + * **Email**: [backintime-project@posteo.de](mailto:backintime-project@posteo.de) - cd common - ./configure - make - make test - sudo make install +# Installation +_Back In Time_ is included in +[many GNU/Linux distributions](https://repology.org/project/backintime/badges). +Use their repositories to install it. If you want to contribute or using the +latest development version of _Back In Time_ please see section +[Build & Install](CONTRIBUTING.md#build--install) in +[`CONTRIBUTING.md`](CONTRIBUTING.md). Also the dependencies are described there. -##### Qt5 GUI +# Known Problems and Workarounds -* dependencies - - x11-utils - - python3-pyqt5 - - libnotify-bin - - policykit-1 - - python3-dbus.mainloop.pyqt5 - - backintime-common +In the latest stable release: +- [File permissions handling and therefore possible non-differential backups](#file-permissions-handling-and-therefore-possible-non-differential-backups) +- [`qt_probing.py` may hang with high CPU usage when running BiT as `root` via `cron`](#qt_probingpy-may-hang-with-high-cpu-usage-when-running-bit-as-root-via-cron) -* recomended - - python3-secretstorage or - - python3-keyring-kwallet or - - python3-gnomekeyring - - kompare or - - meld +More problems described in +[this FAQ section](FAQ.md#problems-errors--solutions). -* Command +## File permissions handling and therefore possible non-differential backups - cd qt - ./configure - make - sudo make install +- In version 1.2.0, the handling of file permissions changed. +- In versions <= 1.1.24 (until 2017) all file permissions were set to + `-rw-r--r--` in the backup target. +- In versions >= 1.2.0 (since 2019) `rsync` is executed with `--perms` option + which tells `rsync` to preserve the source file permission. +Therefore backups can be larger and slower, especially the first backup after +upgrading to a version >= 1.2.0. -## configure options +If you don't like the new behavior, you can use _Expert Options_ -> +_Paste additional options to rsync_ to add `--no-perms --no-group --no-owner` +to it. Note that the exact file permissions can still be found in +`fileinfo.bz2` and are also considered when restoring files. - first value is default: - --no-fuse-group | --fuse-group (only COMMON) - Some distributions require user to be in group 'fuse' to use - sshfs and encfs. This toggles the check on or off. +## `qt_probing.py` may hang with high CPU usage when running BiT as `root` via `cron` - --python3 | --python (all) - Use either 'python3' or 'python' to start Python Version 3.x +See the related issue [#1592](https://github.com/bit-team/backintime/issues/1592). -## NewsFeed +The only reliable work-around is to delete (or move into another directory) +the file `/usr/share/backintime/common/qt_probing.py`: -Back In Time has a RSS feed -https://feeds.launchpad.net/backintime/announcements.atom +`mv /usr/share/backintime/common/qt_probing.py /usr/share/backintime/` + +Renaming does *not* work! -## Contribute +# Contributing and other ways to support the project +See [CONTRIBUTING](CONTRIBUTING.md) file for an overview about the projects +workflow and strategy. -There is a dev-docu on https://backintime-dev.readthedocs.org -It's not complete yet but I'm working on it. If you'd like to contribute -please add docstrings following the -[Google style guide](https://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html) -and add unit-tests for new methods in common. To run unit-test locally you can -run `cd common && ./configure && make test` +# Licenses +Please read [`LICENSES.md`](LICENSES.md). -December 2016 +--- +February 2026 diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 000000000..19fe90efe --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: © 2024 Back In Time Team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory or go to +# and . + +# See https://reuse.software/faq/#bulk-license +version = 1 + +[[annotations]] +path = [ + "AUTHORS", + "VERSION", + "TRANSLATIONS", + "CHANGES", + "common/config-example-*", + "common/test/config", + +] +SPDX-License-Identifier = "CC0-1.0" +SPDX-FileCopyrightText = "© 2008 Back In Time Team" + + +[[annotations]] +path = [ + "doc/manpages/backintime-config.5", +] +SPDX-License-Identifier = "GPL-2.0-or-later" +SPDX-FileCopyrightText = "© 2008 Back In Time Team" diff --git a/TODO b/TODO deleted file mode 100644 index 935311fa3..000000000 --- a/TODO +++ /dev/null @@ -1,4 +0,0 @@ -TODO: -* use rsync return code to check that the snapshot was really taken (no out of space, copy error ...) -* uid/gid translate table with 'Full rsync mode' -* grep DBUS_SESSION_BUS_ADDRESS from session-managers environ (https://github.com/bit-team/backintime/issues/723) diff --git a/TRANSLATIONS b/TRANSLATIONS deleted file mode 100644 index 9b6bbfc66..000000000 --- a/TRANSLATIONS +++ /dev/null @@ -1,9 +0,0 @@ -Spanish: Francisco Manuel García Claramonte -German: Michael Wiedmann -Swedish: Niklas Grahn -Slovenian: Vanja Cvelbar -French: Michel Corps -Slovak: Tomáš Vadina -Polish: Paweł Hołuj -Russian: Vadim Peretokin -Other languages: Launchpad translators diff --git a/VERSION b/VERSION index 3a3cd8cc8..d72f26267 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.1 +2.0.0-dev diff --git a/common/.coveragerc b/common/.coveragerc deleted file mode 100644 index ba2b1797b..000000000 --- a/common/.coveragerc +++ /dev/null @@ -1,10 +0,0 @@ -# default options for coverage from coveralls-python - -[run] -omit = - */virtualenv/python3* - -[report] -exclude_lines = - pragma: no cover - if __name__ == '__main__': diff --git a/common/applicationinstance.py b/common/applicationinstance.py index 63f2e293f..be3e723c0 100644 --- a/common/applicationinstance.py +++ b/common/applicationinstance.py @@ -1,28 +1,29 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +""" +This module holds the ApplicationInstance class, used to handle +the one application instance mechanism. +""" import os import fcntl -import errno - import logger import tools + +# TODO This class name is a misnomer (there may be more than +# one app instance eg. if a restore is running and another +# backup starts). +# Rename it to eg. LockFileManager +# TODO2 When refactoring have a look at "common/flock.py" still implementing +# a contxt manager for that problem. class ApplicationInstance: """ Class used to handle one application instance mechanism. @@ -35,11 +36,12 @@ class ApplicationInstance: is checking at the same time """ - def __init__(self, pidFile, autoExit = True, flock = False): + def __init__(self, pidFile, autoExit=True, flock=False): self.pidFile = pidFile self.pid = 0 self.procname = '' self.flock = None + if flock: self.flockExclusiv() @@ -50,42 +52,43 @@ def __init__(self, pidFile, autoExit = True, flock = False): def __del__(self): self.flockUnlock() - def check(self, autoExit = False): - """ - Check if the current application is already running + # TODO Rename to is_single_instance() to make the purpose more obvious + def check(self, autoExit=False): + """Check if the current application is already running. Args: - autoExit (bool): automatically call sys.exit if there is an other - instance running + autoExit (bool): Automatically call ``sys.exit()`` if there is + another instance running. Returns: - bool: ``True`` if this is the only application - instance + bool: ``True`` if this is the only application instance. """ - #check if the pidfile exists + # check if the pidfile exists if not os.path.isfile(self.pidFile): return True self.pid, self.procname = self.readPidFile() - #check if the process with specified by pid exists + # check if the process (PID) that created the pid file still exists if 0 == self.pid: return True if not tools.processAlive(self.pid): return True - #check if the process has the same procname - #check cmdline for backwards compatibility + # check if the process has the same procname + # check cmdline for backwards compatibility if self.procname and \ self.procname != tools.processName(self.pid) and \ self.procname != tools.processCmdline(self.pid): - return True + return True if autoExit: - #exit the application + # exit the application print("The application is already running !") - exit(0) #exit raise an exception so don't put it in a try/except block + + # exit raise an exception so don't put it in a try/except block + exit(0) return False @@ -108,9 +111,17 @@ def startApplication(self): try: with open(self.pidFile, 'wt') as f: f.write('{}\n{}'.format(pid, procname)) + except OSError as e: - logger.error('Failed to write PID file %s: [%s] %s' %(e.filename, e.errno, e.strerror)) + logger.error( + 'Failed to write PID file %s: [%s] %s' + % (e.filename, e.errno, e.strerror)) + # The flock is removed here because it shall only ensure serialized + # access to the "pidFile" (lock file): Without setting flock in + # __init__ another process could also check for the existences of the + # "pidFile" in parallel and also create a new one (overwriting the one + # created here). self.flockUnlock() def exitApplication(self): @@ -124,14 +135,63 @@ def exitApplication(self): def flockExclusiv(self): """ - Create an exclusive lock to block a second instance while - the first instance is starting. + Create an exclusive advisory file lock named .flock + to block the creation of a second instance while the first instance + is still in the process of starting (but has not yet completely + started). + + The purpose is to make + 1. the check if the PID lock file already exists + 2. and the subsequent creation of the PID lock file + an atomic operation by using a blocking "flock" file lock + on a second file to avoid that two or more processes check for + an existing PID lock file, find none and create a new one + (so that only the last creator wins). + + Dev notes: + ---------- + buhtz (2023-09): + Not sure but just log an ERROR without doing anything else is + IMHO not enough. + + aryoda (2023-12): + It seems the purpose of this additional lock file using an exclusive + lock is to block the other process to continue until this exclusive + lock is released (= serialize execution). + Therefore advisory locks are used via fcntl.flock (see: man 2 fcntl) + + buhtz (2024-05): + Have a look at the new :mod:`flock` module providing an flock context + manager. """ + + flock_file_URI = self.pidFile + '.flock' + logger.debug('Trying to put an advisory lock on the flock ' + f'file {flock_file_URI}') + try: - self.flock = open(self.pidFile + '.flock', 'w') + self.flock = open(flock_file_URI, 'w') + # This opens an advisory lock which which is considered only + # if other processes cooperate by explicitly acquiring locks + # (which BiT does IMHO). + # TODO Does this lock request really block if another processes + # already holds a lock (until the lock is released)? + # = Is the execution serialized? + # Provisional answer: + # Yes, fcntl.flock seems to wait until the lock is removed + # from the file. + # Tested by starting one process in the console via + # python3 applicationinstance.py + # and then (while the first process is still running) + # the same command in a 2nd terminal. + # The ApplicationInstance constructor call needs + # to be changed for this by adding "False, True" + # to trigger an exclusive lock. fcntl.flock(self.flock, fcntl.LOCK_EX) + except OSError as e: - logger.error('Failed to write flock file %s: [%s] %s' %(e.filename, e.errno, e.strerror)) + logger.error('Failed to write flock file %s: [%s] %s' + % (e.filename, e.errno, e.strerror)) def flockUnlock(self): """ @@ -139,14 +199,18 @@ def flockUnlock(self): but should find it self to be obsolete. """ if self.flock: + logger.debug('Trying to remove the advisory lock from the ' + f'flock file {self.flock.name}') fcntl.fcntl(self.flock, fcntl.LOCK_UN) self.flock.close() + try: os.remove(self.flock.name) except: - #an other instance was faster - #race condition while using 'if os.path.exists(...)' + # an other instance was faster + # race condition while using 'if os.path.exists(...)' pass + self.flock = None def readPidFile(self): @@ -158,31 +222,42 @@ def readPidFile(self): """ pid = 0 procname = '' + try: with open(self.pidFile, 'rt') as f: data = f.read() + data = data.split('\n', 1) + if data[0].isdigit(): pid = int(data[0]) + if len(data) > 1: procname = data[1].strip('\n') + except OSError as e: - logger.warning('Failed to read PID and process name from %s: [%s] %s' %(e.filename, e.errno, e.strerror)) + logger.warning( + 'Failed to read PID and process name from %s: [%s] %s' + % (e.filename, e.errno, e.strerror)) + except ValueError as e: - logger.warning('Failed to extract PID and process name from %s: %s' - %(self.pidFile, str(e))) + logger.warning( + 'Failed to extract PID and process name from %s: %s' + % (self.pidFile, str(e))) + return (pid, procname) + if __name__ == '__main__': import time - #create application instance + # create application instance appInstance = ApplicationInstance('/tmp/myapp.pid') - #do something here + # do something here print("Start MyApp") - time.sleep(5) #sleep 5 seconds + time.sleep(5) # sleep 5 seconds print("End MyApp") - #remove pid file + # remove pid file appInstance.exitApplication() diff --git a/common/askpass.py b/common/askpass.py index c4d2e242e..f478a5e94 100644 --- a/common/askpass.py +++ b/common/askpass.py @@ -1,35 +1,22 @@ -# Copyright (C) 2012-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""A helper tool to pipe passwords for SSH/SSHFS/EncFS.""" import os import sys -try: - import gtk -except: - pass - import password import password_ipc import tools import config + if __name__ == '__main__': - """ - return password. - """ + # return password + cfg = config.Config() tools.envLoad(cfg.cronEnvFile()) @@ -39,18 +26,22 @@ if mode == 'USER': prompt = os.getenv('ASKPASS_PROMPT', None) pw = password.Password(cfg) - print(pw.passwordFromUser(None, prompt = prompt)) + print(pw.passwordFromUser(None, prompt=prompt)) + sys.exit(0) temp_file = os.getenv('ASKPASS_TEMP') + if temp_file is None: - #normal mode, get password from module password + # normal mode, get password from module password pw = password.Password(cfg) print(pw.password(None, profile_id, mode)) + sys.exit(0) - #temp mode + # temp mode fifo = password_ipc.FIFO(temp_file) pw = fifo.read(5) + if pw: print(pw) diff --git a/common/backintime b/common/backintime index 455bc9d82..8bfc7237a 100755 --- a/common/backintime +++ b/common/backintime @@ -1,27 +1,33 @@ #!/bin/sh - -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2017 Matthias Gerstner +# SPDX-FileCopyrightText: © 2024 Jürgen Altfeld +# SPDX-FileCopyrightText: © 2024 Christian Buhtz # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# SPDX-License-Identifier: GPL-2.0-or-later # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# Location of this script CUR_PATH="$(dirname $(readlink -m $0))" + +# Was this script started in the source code folder (normally during development)? if [ -f "${CUR_PATH}/backintime.py" ]; then APP_PATH=$CUR_PATH else + # CUR_PATH must be /usr/bin (the default installation path of this script) + # or another sibling folder of "share" (in case of an alternative installation + # folder like "/var/bin" where BiT must be installed into /var/share/backintime then) APP_PATH=$(readlink -m "${CUR_PATH}/../share/backintime/common") fi -python3 -Es $APP_PATH/backintime.py "$@" +# -E ignores env vars like PYTHONPATH and PYTHONHOME that may modify +# the behavior of the interpreter +# -s Don't add user site directory to sys.path +# TODO Should we use "$APP_PATH" (quoted) to prevent globbing and word splitting? +/usr/bin/python3 -Es $APP_PATH/backintime.py "$@" diff --git a/common/backintime-askpass b/common/backintime-askpass index 6ec7a6258..f13a4d68f 100755 --- a/common/backintime-askpass +++ b/common/backintime-askpass @@ -1,24 +1,19 @@ #!/bin/sh - -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2017 Matthias Gerstner +# SPDX-FileCopyrightText: © 2024 Jürgen Altfeld # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# SPDX-License-Identifier: GPL-2.0-or-later # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . -#fixing gray window error -#https://launchpad.net/bugs/1493020 +# fixing gray window error +# https://launchpad.net/bugs/1493020 export QT_GRAPHICSSYSTEM="native" CUR_PATH="$(dirname $(readlink -m $0))" @@ -28,4 +23,4 @@ else APP_PATH=$(readlink -m "${CUR_PATH}/../share/backintime/common") fi -python3 -Es $APP_PATH/askpass.py "$@" +/usr/bin/python3 -Es $APP_PATH/askpass.py "$@" diff --git a/common/backintime.desktop b/common/backintime.desktop index 6694de8b3..9e4b4a557 100644 --- a/common/backintime.desktop +++ b/common/backintime.desktop @@ -1,8 +1,17 @@ +# SPDX-FileCopyrightText: © 2009 Back In Time team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory or go to +# and . [Desktop Entry] -Version=1.0 -Name=Backintime Password Cache +Version=1.5 +Name=Back In Time Password Cache Exec=/bin/sh -c "backintime pw-cache start 2>&1 >/dev/null" -Comment=Cache passwords for non-interactive Backintime cronjobs +Comment=Cache passwords for non-interactive Back In Time cronjobs Icon=gtk-save -Terminal=false +Terminal=NO Type=Application diff --git a/common/backintime.py b/common/backintime.py index 277c05366..0e02d0f44 100644 --- a/common/backintime.py +++ b/common/backintime.py @@ -1,50 +1,30 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os -import sys -import gettext -import argparse -import atexit import subprocess +import pathlib from datetime import datetime -from time import sleep - +import tools +# Workaround for situations where startApp() is not invoked. +# E.g. when using --diagnostics and other argparse.Action +tools.initiate_translation(None) +import bitbase import config import logger -import snapshots -import tools -import sshtools -import mount -import password -import encfstools import cli -from exceptions import MountException -from applicationinstance import ApplicationInstance - -_=gettext.gettext +import cliarguments +from diagnostics import collect_minimal_diagnostics -RETURN_OK = 0 -RETURN_ERR = 1 -RETURN_NO_CFG = 2 -parsers = {} - -def takeSnapshotAsync(cfg, checksum = False): +def takeSnapshotAsync(cfg, checksum=False): """ Fork a new backintime process with 'backup' command which will take a new snapshot in background. @@ -53,1113 +33,150 @@ def takeSnapshotAsync(cfg, checksum = False): cfg (config.Config): config that should be used """ cmd = [] + if cfg.ioniceOnUser(): cmd.extend(('ionice', '-c2', '-n7')) + cmd.append('backintime') + if '1' != cfg.currentProfile(): - cmd.extend(('--profile-id', str(cfg.currentProfile()))) + cmd.extend(('--profile', str(cfg.currentProfile()))) + if cfg._LOCAL_CONFIG_PATH is not cfg._DEFAULT_CONFIG_PATH: cmd.extend(('--config', cfg._LOCAL_CONFIG_PATH)) - if cfg._LOCAL_DATA_FOLDER is not cfg._DEFAULT_LOCAL_DATA_FOLDER: - cmd.extend(('--share-path', cfg.DATA_FOLDER_ROOT)) + + # if cfg._LOCAL_DATA_FOLDER is not cfg._DEFAULT_LOCAL_DATA_FOLDER: + # cmd.extend(('--share-path', cfg.DATA_FOLDER_ROOT)) + if logger.DEBUG: cmd.append('--debug') + if checksum: cmd.append('--checksum') + cmd.append('backup') # child process need to start its own ssh-agent because otherwise # it would be lost without ssh-agent if parent will close env = os.environ.copy() + for i in ('SSH_AUTH_SOCK', 'SSH_AGENT_PID'): try: del env[i] + except: pass - subprocess.Popen(cmd, env = env) - -def takeSnapshot(cfg, force = True): - """ - Take a new snapshot. - - Args: - cfg (config.Config): config that should be used - force (bool): take the snapshot even if it wouldn't need to - or would be prevented (e.g. running on battery) - - Returns: - bool: ``True`` if there was an error - """ - tools.envLoad(cfg.cronEnvFile()) - ret = snapshots.Snapshots(cfg).backup(force) - return ret - -def _mount(cfg): - """ - Mount external filesystems. - - Args: - cfg (config.Config): config that should be used - """ - try: - hash_id = mount.Mount(cfg = cfg).mount() - except MountException as ex: - logger.error(str(ex)) - sys.exit(RETURN_ERR) - else: - cfg.setCurrentHashId(hash_id) - -def _umount(cfg): - """ - Unmount external filesystems. - - Args: - cfg (config.Config): config that should be used - """ - try: - mount.Mount(cfg = cfg).umount(cfg.current_hash_id) - except MountException as ex: - logger.error(str(ex)) - -def createParsers(app_name = 'backintime'): - """ - Define parsers for commandline arguments. - - Args: - app_name (str): string representing the current application - """ - global parsers - - #define debug - debugArgsParser = argparse.ArgumentParser(add_help = False) - debugArgsParser.add_argument('--debug', - action = 'store_true', - help = 'Increase verbosity.') - - #define config argument - configArgsParser = argparse.ArgumentParser(add_help = False) - configArgsParser.add_argument('--config', - metavar = 'PATH', - type = str, - action = 'store', - help = 'Read config from %(metavar)s. ' + - 'Default = ~/.config/backintime/config') - - configArgsParser.add_argument('--share-path', - metavar = 'PATH', - type = str, - action = 'store', - help = 'Write runtime data (locks, messages, log and mountpoints) to %(metavar)s.') - - #define common arguments which are used for all commands - commonArgsParser = argparse.ArgumentParser(add_help = False, parents = [configArgsParser, debugArgsParser]) - profileGroup = commonArgsParser.add_mutually_exclusive_group() - profileGroup.add_argument ('--profile', - metavar = 'NAME', - type = str, - action = 'store', - help = 'Select profile by %(metavar)s.') - profileGroup.add_argument ('--profile-id', - metavar = 'ID', - type = int, - action = 'store', - help = 'Select profile by %(metavar)s.') - commonArgsParser.add_argument('--quiet', - action = 'store_true', - help = 'Be quiet. Suppress messages on stdout.') - - #define arguments which are only used by snapshots-path, snapshots-list-path and last-snapshot-path - snapshotPathParser = argparse.ArgumentParser(add_help = False) - snapshotPathParser.add_argument('--keep-mount', - action = 'store_true', - help = "Don't unmount on exit.") - - #define arguments which are used by rsync commands (backup and restore) - rsyncArgsParser = argparse.ArgumentParser(add_help = False) - rsyncArgsParser.add_argument('--checksum', - action = 'store_true', - help = 'force to use checksum for checking if files have been changed.') - - #define arguments for snapshot remove - removeArgsParser = argparse.ArgumentParser(add_help = False) - removeArgsParser.add_argument('SNAPSHOT_ID', - type = str, - action = 'store', - nargs = '*', - help = 'ID of snapshots which should be removed.') - - #define main argument parser - parser = argparse.ArgumentParser(prog = app_name, - parents = [commonArgsParser], - description = '%(app)s - a simple backup tool for Linux.' - % {'app': config.Config.APP_NAME}, - epilog = "For backwards compatibility commands can also be used with trailing '--'. " - "All listed arguments will work with all commands. Some commands have extra arguments. " - "Run '%(app_name)s -h' to see the extra arguments." - % {'app_name': app_name}) - parsers['main'] = parser - parser.add_argument('--version', '-v', - action = 'version', - version = '%(prog)s ' + str(config.Config.VERSION), - help = "show %(prog)s's version number.") - parser.add_argument('--license', - action = printLicense, - nargs = 0, - help = "show %(prog)s's license.") - - ####################### - ### define commands ### - ####################### - epilog = "Run '%(app_name)s -h' to get help for additional arguments. " %{'app_name': app_name} - epilogCommon = epilog + 'Additional arguments: --config, --debug, --profile, --profile-id, --quiet' - epilogConfig = epilog + 'Additional arguments: --config, --debug' - - subparsers = parser.add_subparsers(title = 'Commands', dest = 'command') - command = 'backup' - nargs = 0 - aliases = [(command, nargs), ('b', nargs)] - description = 'Take a new snapshot. Ignore if the profile ' +\ - 'is not scheduled or if the machine runs on battery.' - backupCP = subparsers.add_parser(command, - parents = [rsyncArgsParser], - epilog = epilogCommon, - help = description, - description = description) - backupCP.set_defaults(func = backup) - parsers[command] = backupCP - - command = 'backup-job' - nargs = 0 - aliases.append((command, nargs)) - description = 'Take a new snapshot in background only ' +\ - 'if the profile is scheduled and the machine ' +\ - 'is not on battery. This is use by cron jobs.' - backupJobCP = subparsers.add_parser(command, - parents = [rsyncArgsParser], - epilog = epilogCommon, - help = description, - description = description) - backupJobCP.set_defaults(func = backupJob) - parsers[command] = backupJobCP - - command = 'benchmark-cipher' - nargs = '?' - aliases.append((command, nargs)) - description = 'Show a benchmark of all ciphers for ssh transfer.' - benchmarkCipherCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - benchmarkCipherCP.set_defaults(func = benchmarkCipher) - parsers[command] = benchmarkCipherCP - benchmarkCipherCP.add_argument ('FILE_SIZE', - type = int, - action = 'store', - default = 40, - nargs = '?', - help = 'File size used to for benchmark.') - - command = 'check-config' - description = 'Check the profiles configuration and install crontab entries.' - checkConfigCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - checkConfigCP.add_argument ('--no-crontab', - action = 'store_true', - help = 'Do not install crontab entries.') - checkConfigCP.set_defaults(func = checkConfig) - parsers[command] = checkConfigCP - - command = 'decode' - nargs = '*' - aliases.append((command, nargs)) - description = "Decode paths with 'encfsctl decode'" - decodeCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - decodeCP.set_defaults(func = decode) - parsers[command] = decodeCP - decodeCP.add_argument ('PATH', - type = str, - action = 'store', - nargs = '*', - help = 'Decode PATH. If no PATH is specified on command line ' +\ - 'a list of filenames will be read from stdin.') - - command = 'last-snapshot' - nargs = 0 - aliases.append((command, nargs)) - description = 'Show the ID of the last snapshot.' - lastSnapshotCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - lastSnapshotCP.set_defaults(func = lastSnapshot) - parsers[command] = lastSnapshotCP - - command = 'last-snapshot-path' - nargs = 0 - aliases.append((command, nargs)) - description = 'Show the path of the last snapshot.' - lastSnapshotsPathCP = subparsers.add_parser(command, - parents = [snapshotPathParser], - epilog = epilogCommon, - help = description, - description = description) - lastSnapshotsPathCP.set_defaults(func = lastSnapshotPath) - parsers[command] = lastSnapshotsPathCP - - command = 'pw-cache' - nargs = '*' - aliases.append((command, nargs)) - description = 'Control Password Cache for non-interactive cronjobs.' - pwCacheCP = subparsers.add_parser(command, - epilog = epilogConfig, - help = description, - description = description) - pwCacheCP.set_defaults(func = pwCache) - parsers[command] = pwCacheCP - pwCacheCP.add_argument ('ACTION', - action = 'store', - choices = ['start', 'stop', 'restart', 'reload', 'status'], - nargs = '?', - help = 'Command to send to Password Cache daemon.') - - command = 'remove' - nargs = '*' - aliases.append((command, nargs)) - description = 'Remove a snapshot.' - removeCP = subparsers.add_parser(command, - parents = [removeArgsParser], - epilog = epilogCommon, - help = description, - description = description) - removeCP.set_defaults(func = remove) - parsers[command] = removeCP - - command = 'remove-and-do-not-ask-again' - nargs = '*' - aliases.append((command, nargs)) - description = "Remove snapshots and don't ask for confirmation before. Be careful!" - removeDoNotAskCP = subparsers.add_parser(command, - parents = [removeArgsParser], - epilog = epilogCommon, - help = description, - description = description) - removeDoNotAskCP.set_defaults(func = removeAndDoNotAskAgain) - parsers[command] = removeDoNotAskCP - - command = 'restore' - nargs = '*' - aliases.append((command, nargs)) - description = 'Restore files.' - restoreCP = subparsers.add_parser(command, - parents = [rsyncArgsParser], - epilog = epilogCommon, - help = description, - description = description) - restoreCP.set_defaults(func = restore) - parsers[command] = restoreCP - backupGroup = restoreCP.add_mutually_exclusive_group() - restoreCP.add_argument ('WHAT', - type = str, - action = 'store', - nargs = '?', - help = 'Restore file or folder WHAT.') - - restoreCP.add_argument ('WHERE', - type = str, - action = 'store', - nargs = '?', - help = "Restore to WHERE. An empty argument '' will restore to original destination.") - - restoreCP.add_argument ('SNAPSHOT_ID', - type = str, - action = 'store', - nargs = '?', - help = 'Which SNAPSHOT_ID should be used. This can be a snapshot ID or ' +\ - 'an integer starting with 0 for the last snapshot, 1 for the second to last, ... ' +\ - 'the very first snapshot is -1') - - restoreCP.add_argument ('--delete', - action = 'store_true', - help = 'Restore and delete newer files which are not in the snapshot. ' +\ - 'WARNING: deleting files in filesystem root could break your whole system!!!') - - backupGroup.add_argument ('--local-backup', - action = 'store_true', - help = 'Create backup files before changing local files.') - - backupGroup.add_argument ('--no-local-backup', - action = 'store_true', - help = 'Temporary disable creation of backup files before changing local files. ' +\ - 'This can be switched of permanently in Settings, too.') - - restoreCP.add_argument ('--only-new', - action = 'store_true', - help = 'Only restore files which does not exist or are newer than ' +\ - 'those in destination. Using "rsync --update" option.') - - command = 'shutdown' - nargs = 0 - description = 'Shutdown the computer after the snapshot is done.' - shutdownCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - shutdownCP.set_defaults(func = shutdown) - parsers[command] = shutdownCP - - command = 'smart-remove' - nargs = 0 - description = 'Remove snapshots based on "Smart Remove" pattern.' - smartRemoveCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - smartRemoveCP.set_defaults(func = smartRemove) - parsers[command] = smartRemoveCP - - command = 'snapshots-list' - nargs = 0 - aliases.append((command, nargs)) - description = 'Show a list of snapshots IDs.' - snapshotsListCP = subparsers.add_parser(command, - parents = [snapshotPathParser], - epilog = epilogCommon, - help = description, - description = description) - snapshotsListCP.set_defaults(func = snapshotsList) - parsers[command] = snapshotsListCP - - command = 'snapshots-list-path' - nargs = 0 - aliases.append((command, nargs)) - description = "Show the path's to snapshots." - snapshotsListPathCP = subparsers.add_parser(command, - parents = [snapshotPathParser], - epilog = epilogCommon, - help = description, - description = description) - snapshotsListPathCP.set_defaults(func = snapshotsListPath) - parsers[command] = snapshotsListPathCP - - command = 'snapshots-path' - nargs = 0 - aliases.append((command, nargs)) - description = 'Show the path where snapshots are stored.' - snapshotsPathCP = subparsers.add_parser(command, - parents = [snapshotPathParser], - epilog = epilogCommon, - help = description, - description = description) - snapshotsPathCP.set_defaults(func = snapshotsPath) - parsers[command] = snapshotsPathCP - - command = 'unmount' - nargs = 0 - aliases.append((command, nargs)) - description = 'Unmount the profile.' - unmountCP = subparsers.add_parser(command, - epilog = epilogCommon, - help = description, - description = description) - unmountCP.set_defaults(func = unmount) - parsers[command] = unmountCP - - #define aliases for all commands with trailing -- - group = parser.add_mutually_exclusive_group() - for alias, nargs in aliases: - if len(alias) == 1: - arg = '-%s' % alias - else: - arg = '--%s' % alias - group.add_argument(arg, - nargs = nargs, - action = PseudoAliasAction, - help = argparse.SUPPRESS) - -def startApp(app_name = 'backintime'): - """ - Start the requested command or return config if there was no command - in arguments. - - Args: - app_name (str): string representing the current application - - Returns: - config.Config: current config if no command was given in arguments - """ - createParsers(app_name) - #open log - logger.APP_NAME = app_name - logger.openlog() - - #parse args - args = argParse(None) - - #add source path to $PATH environ if running from source - if tools.runningFromSource(): - tools.addSourceToPathEnviron() - - #warn about sudo - if tools.usingSudo() and os.getenv('BIT_SUDO_WARNING_PRINTED', 'false') == 'false': - os.putenv('BIT_SUDO_WARNING_PRINTED', 'true') - logger.warning("It looks like you're using 'sudo' to start %(app)s. " - "This will cause some troubles. Please use either 'sudo -i %(app_name)s' " - "or 'pkexec %(app_name)s'." - %{'app_name': app_name, 'app': config.Config.APP_NAME}) - - #call commands - if 'func' in dir(args): - args.func(args) - else: - setQuiet(args) - printHeader() - return getConfig(args, False) - -def argParse(args): - """ - Parse arguments given on commandline. - - Args: - args (argparse.Namespace): Namespace that should be enhanced - or ``None`` - - Returns: - argparser.Namespace: new parsed Namespace - """ - def join(args, subArgs): - """ - Add new arguments to existing Namespace. - - Args: - args (argparse.Namespace): - main Namespace that should get new arguments - subArgs (argparse.Namespace): - second Namespace which have new arguments - that should be merged into ``args`` - """ - for key, value in vars(subArgs).items(): - #only add new values if it isn't set already or if there really IS a value - if getattr(args, key, None) is None or value: - setattr(args, key, value) - - #first parse the main parser without subparsers - #otherwise positional args in subparsers will be to greedy - #but only if -h or --help is not involved because otherwise - #help will not work for subcommands - mainParser = parsers['main'] - sub = [] - if '-h' not in sys.argv and '--help' not in sys.argv: - for i in mainParser._actions: - if isinstance(i, argparse._SubParsersAction): - #remove subparsers - mainParser._remove_action(i) - sub.append(i) - args, unknownArgs = mainParser.parse_known_args(args) - #read subparsers again - if sub: - [mainParser._add_action(i) for i in sub] - - #parse it again for unknown args - if unknownArgs: - subArgs, unknownArgs = mainParser.parse_known_args(unknownArgs) - join(args, subArgs) - - #finally parse only the command parser, otherwise we miss - #some arguments from command - if unknownArgs and 'command' in args and args.command in parsers: - commandParser = parsers[args.command] - subArgs, unknownArgs = commandParser.parse_known_args(unknownArgs) - join(args, subArgs) - - if 'debug' in args: - logger.DEBUG = args.debug - - dargs = vars(args) - logger.debug('Arguments: %s | unknownArgs: %s' - %({arg:dargs[arg] for arg in dargs if dargs[arg]}, - unknownArgs)) - - #report unknown arguments - #but not if we run aliasParser next because we will parse again in there - if unknownArgs and not ('func' in args and args.func is aliasParser): - mainParser.error('Unknown Argument(s): %s' % ', '.join(unknownArgs)) - return args - -def printHeader(): - """ - Print application name, version and legal notes. - """ - version = config.Config.VERSION - ref, hashid = tools.gitRevisionAndHash() - if ref: - version += " git branch '{}' hash '{}'".format(ref, hashid) - print('') - print('Back In Time') - print('Version: ' + version) - print('') - print('Back In Time comes with ABSOLUTELY NO WARRANTY.') - print('This is free software, and you are welcome to redistribute it') - print("under certain conditions; type `backintime --license' for details.") - print('') - -class PseudoAliasAction(argparse.Action): - """ - Translate '--COMMAND' into 'COMMAND' for backwards compatibility. - """ - def __call__(self, parser, namespace, values, option_string=None): - """ - Translate '--COMMAND' into 'COMMAND' for backwards compatibility. - - Args: - parser (argparse.ArgumentParser): NotImplemented - namespace (argparse.Namespace): Namespace that should get modified - values: NotImplemented - option_string: NotImplemented - """ - #TODO: find a more elegant way to solve this - dest = self.dest.replace('_', '-') - if self.dest == 'b': - replace = '-b' - alias = 'backup' - else: - replace = '--%s' % dest - alias = dest - setattr(namespace, 'func', aliasParser) - setattr(namespace, 'replace', replace) - setattr(namespace, 'alias', alias) - -def aliasParser(args): - """ - Call commands which where given with leading -- for backwards - compatibility. - - Args: - args (argparse.Namespace): - previously parsed arguments - """ - if not args.quiet: - logger.info("Run command '%(alias)s' instead of argument '%(replace)s' due to backwards compatibility." - % {'alias': args.alias, 'replace': args.replace}) - argv = [w.replace(args.replace, args.alias) for w in sys.argv[1:]] - newArgs = argParse(argv) - if 'func' in dir(newArgs): - newArgs.func(newArgs) - -def getConfig(args, check = True): - """ - Load config and change to profile selected on commandline. - - Args: - args (argparse.Namespace): - previously parsed arguments - check (bool): if ``True`` check if config is valid - - Returns: - config.Config: current config with requested profile selected - - Raises: - SystemExit: 1 if ``profile`` or ``profile_id`` is no valid profile - 2 if ``check`` is ``True`` and config is not configured - """ - cfg = config.Config(config_path = args.config, data_path = args.share_path) - logger.debug('config file: %s' % cfg._LOCAL_CONFIG_PATH) - logger.debug('share path: %s' % cfg._LOCAL_DATA_FOLDER) - logger.debug('profiles: %s' % ', '.join('%s=%s' % (x, cfg.profileName(x)) - for x in cfg.profiles())) - - if 'profile_id' in args and args.profile_id: - if not cfg.setCurrentProfile(args.profile_id): - logger.error('Profile-ID not found: %s' % args.profile_id) - sys.exit(RETURN_ERR) - if 'profile' in args and args.profile: - if not cfg.setCurrentProfileByName(args.profile): - logger.error('Profile not found: %s' % args.profile) - sys.exit(RETURN_ERR) - if check and not cfg.isConfigured(): - logger.error('%(app)s is not configured!' %{'app': cfg.APP_NAME}) - sys.exit(RETURN_NO_CFG) - if 'checksum' in args: - cfg.forceUseChecksum = args.checksum - return cfg - -def setQuiet(args): - """ - Redirect :py:data:`sys.stdout` to ``/dev/null`` if ``--quiet`` was set on - commandline. Return the original :py:data:`sys.stdout` file object which can - be used to print absolute necessary information. - - Args: - args (argparse.Namespace): - previously parsed arguments - - Returns: - sys.stdout: default sys.stdout - """ - force_stdout = sys.stdout - if args.quiet: - # do not replace with subprocess.DEVNULL - will not work - sys.stdout = open(os.devnull, 'w') - atexit.register(sys.stdout.close) - atexit.register(force_stdout.close) - return force_stdout - -class printLicense(argparse.Action): - """ - Print custom license - """ - def __init__(self, *args, **kwargs): - super(printLicense, self).__init__(*args, **kwargs) - - def __call__(self, *args, **kwargs): - cfg = config.Config() - print(cfg.license()) - sys.exit(RETURN_OK) - -def backup(args, force = True): - """ - Command for force taking a new snapshot. - - Args: - args (argparse.Namespace): - previously parsed arguments - force (bool): take the snapshot even if it wouldn't need to or would - be prevented (e.g. running on battery) - - Raises: - SystemExit: 0 if successful, 1 if not - """ - setQuiet(args) - printHeader() - cfg = getConfig(args) - ret = takeSnapshot(cfg, force) - sys.exit(int(ret)) - -def backupJob(args): - """ - Command for taking a new snapshot in background. Mainly used for cronjobs. - This will run the snapshot inside a daemon and detach from it. It will - return immediately back to commandline. - - Args: - args (argparse.Namespace): - previously parsed arguments - - Raises: - SystemExit: 0 - """ - cli.BackupJobDaemon(backup, args).start() - -def shutdown(args): - """ - Command for shutting down the computer after the current snapshot has - finished. - - Args: - args (argparse.Namespace): - previously parsed arguments - - Raises: - SystemExit: 0 if successful; 1 if it failed either because there is - no active snapshot for this profile or shutdown is not - supported. - """ - setQuiet(args) - printHeader() - cfg = getConfig(args) - - sd = tools.ShutDown() - if not sd.canShutdown(): - logger.warning('Shutdown is not supported.') - sys.exit(RETURN_ERR) - - instance = ApplicationInstance(cfg.takeSnapshotInstanceFile(), False) - profile = '='.join((cfg.currentProfile(), cfg.profileName())) - if not instance.busy(): - logger.info('There is no active snapshot for profile %s. Skip shutdown.' - %profile) - sys.exit(RETURN_ERR) - - print('Shutdown is waiting for the snapshot in profile %s to end.\nPress CTRL+C to interrupt shutdown.\n' - %profile) - sd.activate_shutdown = True - try: - while instance.busy(): - logger.debug('Snapshot is still active. Wait for shutdown.') - sleep(5) - except KeyboardInterrupt: - print('Shutdown interrupted.') - else: - logger.info('Shutdown now.') - sd.shutdown() - sys.exit(RETURN_OK) - -def snapshotsPath(args): - """ - Command for printing the full snapshot path of current profile. - - Args: - args (argparse.Namespace): - previously parsed arguments - - Raises: - SystemExit: 0 - """ - force_stdout = setQuiet(args) - cfg = getConfig(args) - if args.keep_mount: - _mount(cfg) - if args.quiet: - msg = '{}' - else: - msg = 'SnapshotsPath: {}' - print(msg.format(cfg.snapshotsFullPath()), file=force_stdout) - sys.exit(RETURN_OK) - -def snapshotsList(args): - """ - Command for printing a list of all snapshots in current profile. - - Args: - args (argparse.Namespace): - previously parsed arguments - - Raises: - SystemExit: 0 - """ - force_stdout = setQuiet(args) - cfg = getConfig(args) - _mount(cfg) - if args.quiet: - msg = '{}' - else: - msg = 'SnapshotID: {}' - no_sids = True - #use snapshots.listSnapshots instead of iterSnapshots because of sorting - for sid in snapshots.listSnapshots(cfg, reverse = False): - print(msg.format(sid), file=force_stdout) - no_sids = False - if no_sids: - logger.error("There are no snapshots in '%s'" % cfg.profileName()) - if not args.keep_mount: - _umount(cfg) - sys.exit(RETURN_OK) - -def snapshotsListPath(args): - """ - Command for printing a list of all snapshots pathes in current profile. - - Args: - args (argparse.Namespace): - previously parsed arguments + subprocess.Popen(cmd, env=env) - Raises: - SystemExit: 0 - """ - force_stdout = setQuiet(args) - cfg = getConfig(args) - _mount(cfg) - if args.quiet: - msg = '{}' - else: - msg = 'SnapshotPath: {}' - no_sids = True - #use snapshots.listSnapshots instead of iterSnapshots because of sorting - for sid in snapshots.listSnapshots(cfg, reverse = False): - print(msg.format(sid.path()), file=force_stdout) - no_sids = False - if no_sids: - logger.error("There are no snapshots in '%s'" % cfg.profileName()) - if not args.keep_mount: - _umount(cfg) - sys.exit(RETURN_OK) +def encfs_deprecation_warning(): + """Warn about encfs deprecation in syslog. -def lastSnapshot(args): + See Issue #1734 for details. This function is a workraound and will be + removed if #1734 is closed. """ - Command for printing the very last snapshot in current profile. - Args: - args (argparse.Namespace): - previously parsed arguments + # Don't warn if EncFS isn't installed + if not tools.checkCommand('encfs'): + return - Raises: - SystemExit: 0 - """ - force_stdout = setQuiet(args) - cfg = getConfig(args) - _mount(cfg) - sid = snapshots.lastSnapshot(cfg) - if sid: - if args.quiet: - msg = '{}' - else: - msg = 'SnapshotID: {}' - print(msg.format(sid), file=force_stdout) + # Timestamp file + xdg_state = os.environ.get('XDG_STATE_HOME', None) + if xdg_state: + xdg_state = pathlib.Path(xdg_state) else: - logger.error("There are no snapshots in '%s'" % cfg.profileName()) - _umount(cfg) - sys.exit(RETURN_OK) + xdg_state = pathlib.Path.home() / '.local' / 'state' + fp = xdg_state / 'backintime.encfs-warning.timestamp' -def lastSnapshotPath(args): - """ - Command for printing the path of the very last snapshot in - current profile. + if fp.exists(): + # Calculate age of that file + delta = datetime.now() - datetime.fromtimestamp(fp.stat().st_mtime) - Args: - args (argparse.Namespace): - previously parsed arguments + # Don't warn if to young + if delta.days < 30: + return - Raises: - SystemExit: 0 - """ - force_stdout = setQuiet(args) - cfg = getConfig(args) - _mount(cfg) - sid = snapshots.lastSnapshot(cfg) - if sid: - if args.quiet: - msg = '{}' - else: - msg = 'SnapshotPath: {}' - print(msg.format(sid.path()), file=force_stdout) else: - logger.error("There are no snapshots in '%s'" % cfg.profileName()) - if not args.keep_mount: - _umount(cfg) - sys.exit(RETURN_OK) + # ensure existence + fp.parent.mkdir(parents=True, exist_ok=True) -def unmount(args): - """ - Command for unmounting all filesystems. + logger.error( + 'EncFS encrypted profiles are no longer supported in Back In Time. ' + 'Existing profiles can be used. New ones can not be created. EncFS ' + 'support will be completely removed in a future release (expected ' + f'around 2027). For details read: {bitbase.URL_ENCRYPT_TRANSITION}' + ) - Args: - args (argparse.Namespace): - previously parsed arguments + # refresh timestamp + fp.touch() - Raises: - SystemExit: 0 - """ - setQuiet(args) - cfg = getConfig(args) - _mount(cfg) - _umount(cfg) - sys.exit(RETURN_OK) -def benchmarkCipher(args): +def startApp(bin_name: str) -> config.Config | None: """ - Command for transferring a file with scp to remote host with all - available ciphers and print its speed and time. + Start the requested command or return config. - Args: - args (argparse.Namespace): - previously parsed arguments - - Raises: - SystemExit: 0 - """ - setQuiet(args) - printHeader() - cfg = getConfig(args) - if cfg.snapshotsMode() in ('ssh', 'ssh_encfs'): - ssh = sshtools.SSH(cfg) - ssh.benchmarkCipher(args.FILE_SIZE) - sys.exit(RETURN_OK) - else: - logger.error("SSH is not configured for profile '%s'!" % cfg.profileName()) - sys.exit(RETURN_ERR) - -def pwCache(args): - """ - Command for starting password cache daemon. - - Args: - args (argparse.Namespace): - previously parsed arguments - - Raises: - SystemExit: 0 if daemon is running, 1 if not - """ - force_stdout = setQuiet(args) - printHeader() - cfg = getConfig(args) - ret = RETURN_OK - daemon = password.Password_Cache(cfg) - if args.ACTION and args.ACTION != 'status': - getattr(daemon, args.ACTION)() - elif args.ACTION == 'status': - print('%(app)s Password Cache: ' % {'app': cfg.APP_NAME}, end=' ', file = force_stdout) - if daemon.status(): - print(cli.bcolors.OKGREEN + 'running' + cli.bcolors.ENDC, file = force_stdout) - ret = RETURN_OK - else: - print(cli.bcolors.FAIL + 'not running' + cli.bcolors.ENDC, file = force_stdout) - ret = RETURN_ERR - else: - daemon.run() - sys.exit(ret) - -def decode(args): - """ - Command for decoding paths given paths with 'encfsctl'. - Will listen on stdin if no path was given. + Command (e.g. 'backup') is specified via command line argument. + Without command the current config is returned instead. Args: - args (argparse.Namespace): - previously parsed arguments + bin_name: The binaries name of current application. - Raises: - SystemExit: 0 - """ - force_stdout = setQuiet(args) - cfg = getConfig(args) - if cfg.snapshotsMode() not in ('local_encfs', 'ssh_encfs'): - logger.error("Profile '%s' is not encrypted." % cfg.profileName()) - sys.exit(RETURN_ERR) - _mount(cfg) - d = encfstools.Decode(cfg) - if not args.PATH: - while True: - try: - path = input() - except EOFError: - break - if not path: - break - print(d.path(path), file = force_stdout) - else: - print('\n'.join(d.list(args.PATH)), file = force_stdout) - d.close() - _umount(cfg) - sys.exit(RETURN_OK) - -def remove(args, force = False): - """ - Command for removing snapshots. - - Args: - args (argparse.Namespace): - previously parsed arguments - force (bool): don't ask before removing (BE CAREFUL!) - - Raises: - SystemExit: 0 - """ - setQuiet(args) - printHeader() - cfg = getConfig(args) - _mount(cfg) - cli.remove(cfg, args.SNAPSHOT_ID, force) - _umount(cfg) - sys.exit(RETURN_OK) - -def removeAndDoNotAskAgain(args): + Returns: + Current configuration instance if command is missing. """ - Command for removing snapshots without asking before remove - (BE CAREFUL!) + parser_agent = cliarguments.ParserAgent( + app_name=bitbase.APP_NAME, bin_name=bin_name) - Args: - args (argparse.Namespace): - previously parsed arguments + syslog_id_suffix = { + bitbase.BINARY_NAME_CLI: 'CLI', + bitbase.BINARY_NAME_GUI: 'GUI' + }[bin_name] - Raises: - SystemExit: 0 - """ - remove(args, True) + logger.openlog(syslog_id_suffix) -def smartRemove(args): - """ - Command for running Smart-Remove from Terminal. + args = cliarguments.parse_arguments(args=None, agent=parser_agent) - Args: - args (argparse.Namespace): - previously parsed arguments + # Name, Version, As Root, OS + msg = '' + for key, val in collect_minimal_diagnostics().items(): + msg = f'{msg}; {key}: {val}' + logger.debug(msg[2:]) - Raises: - SystemExit: 0 if okay - 2 if Smart-Remove is not configured - """ - setQuiet(args) - printHeader() - cfg = getConfig(args) - sn = snapshots.Snapshots(cfg) + # Add source path to $PATH environ if running from source + if tools.runningFromSource(): + tools.addSourceToPathEnviron() - enabled, keep_all, keep_one_per_day, keep_one_per_week, keep_one_per_month = cfg.smartRemove() - if enabled: - _mount(cfg) - del_snapshots = sn.smartRemoveList(datetime.today(), - keep_all, - keep_one_per_day, - keep_one_per_week, - keep_one_per_month) - logger.info('Smart Remove will remove {} snapshots'.format(len(del_snapshots))) - sn.smartRemove(del_snapshots, log = logger.info) - _umount(cfg) - sys.exit(RETURN_OK) - else: - logger.error('Smart Remove is not configured.') - sys.exit(RETURN_NO_CFG) + # Warn about sudo + if (os.getenv('SUDO_USER') # exists only if sudo was used + and os.getenv('BIT_SUDO_WARNING_PRINTED', 'false') == 'false'): -def restore(args): - """ - Command for restoring files from snapshots. + os.putenv('BIT_SUDO_WARNING_PRINTED', 'true') + logger.warning( + "It looks like you're using 'sudo' to start " + f"{config.Config.APP_NAME}. This will cause some trouble. " + f"Please use either 'sudo -i {bin_name}' or 'pkexec {bin_name}'.") - Args: - args (argparse.Namespace): - previously parsed arguments + encfs_deprecation_warning() - Raises: - SystemExit: 0 - """ - setQuiet(args) - printHeader() - cfg = getConfig(args) - _mount(cfg) - if cfg.backupOnRestore() and not args.no_local_backup: - backup = True - else: - backup = args.local_backup - cli.restore(cfg, - args.SNAPSHOT_ID, - args.WHAT, - args.WHERE, - delete = args.delete, - backup = backup, - only_new = args.only_new) - _umount(cfg) - sys.exit(RETURN_OK) + # Call commands + if 'func' in dir(args): + args.func(args) + return None -def checkConfig(args): - """ - Command for checking the config file. + # No arguments/commands + cli.set_quiet(args) + cli.print_header() - Args: - args (argparse.Namespace): - previously parsed arguments + return cli.get_config_and_select_profile( + config_path=args.config, + data_path=args.share_path, + profile=args.profile, + # Dev note (buhtz, 2025): There is not a default value in all cases, + # because "--checksum" is exclusive to rsync-related commands. + checksum=getattr(args, 'checksum', None), + check=False) - Raises: - SystemExit: 0 if config is okay, 1 if not - """ - force_stdout = setQuiet(args) - printHeader() - cfg = getConfig(args) - if cli.checkConfig(cfg, crontab = not args.no_crontab): - print("\nConfig %(cfg)s profile '%(profile)s' is fine." - % {'cfg': cfg._LOCAL_CONFIG_PATH, - 'profile': cfg.profileName()}, - file = force_stdout) - sys.exit(RETURN_OK) - else: - print("\nConfig %(cfg)s profile '%(profile)s' has errors." - % {'cfg': cfg._LOCAL_CONFIG_PATH, - 'profile': cfg.profileName()}, - file = force_stdout) - sys.exit(RETURN_ERR) if __name__ == '__main__': - startApp() + startApp(bin_name=bitbase.BINARY_NAME_CLI) diff --git a/common/bash-completion/backintime b/common/bash-completion/backintime index 775a82aa3..34411a332 100644 --- a/common/bash-completion/backintime +++ b/common/bash-completion/backintime @@ -1,10 +1,18 @@ -#extract profile and config arguments +# SPDX-FileCopyrightText: © 2015 Germar Reitze +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . + +# extract profile and config arguments _bit_extr_opts() { local c=0 last="" opts="" while [[ $c -le ${COMP_CWORD} ]]; do case "${last}" in - --profile|--profile-id|--config) + --profile|--config) if [[ ${COMP_WORDS[$c]} != -* ]]; then opts="${opts} ${last} ${COMP_WORDS[$c]}" fi ;; @@ -15,10 +23,10 @@ _bit_extr_opts() echo "${opts}" } -#return a list of all snapshots +# return a list of all snapshots _bit_snapshots_list() { - backintime$(_bit_extr_opts) --quiet snapshots-list | awk '{print $2}' + backintime$(_bit_extr_opts) --quiet show | awk '{print $2}' } _backintime() @@ -28,16 +36,16 @@ _backintime() COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="--profile --profile-id --quiet --config --version --license \ - --help --debug --checksum --no-crontab --keep-mount --delete \ - --local-backup --no-local-backup --only-new --share-path" - actions="backup backup-job snapshots-path snapshots-list \ - snapshots-list-path last-snapshot last-snapshot-path unmount \ - benchmark-cipher pw-cache decode remove restore check-config \ - smart-remove shutdown" + opts="--profile --quiet --config --version --license \ + --help --debug --checksum --no-crontab --delete \ + --local-backup --no-local-backup --only-new \ + --diagnostics --last --path --background" + actions="backup show unmount \ + pw-cache remove restore check-config \ + prune shutdown" pw_cache_commands="start stop restart reload status" - #extract the current action + # extract the current action while [[ $c -le $[${COMP_CWORD} - 1] ]]; do case ${actions} in *"${COMP_WORDS[$c]}"*) @@ -82,7 +90,7 @@ _backintime() esac case "${prev}" in - --config|decode|restore|--share-path) + --config|decode|restore) if [[ ${cur} != -* ]]; then _filedir return 0 @@ -103,3 +111,4 @@ _backintime() } complete -F _backintime backintime complete -F _backintime backintime-qt + diff --git a/common/bcolors.py b/common/bcolors.py index 523fb1944..cfdc922c9 100644 --- a/common/bcolors.py +++ b/common/bcolors.py @@ -1,28 +1,19 @@ -#!/usr/bin/env python3 -# Copyright (C) 2015-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2015-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import sys if sys.stdout.isatty(): - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' + HEADER = '\033[95m' # light magenta + OKBLUE = '\033[94m' # light blue + OKGREEN = '\033[92m' # light green + WARNING = '\033[93m' # light yellow + FAIL = '\033[91m' # light red + CRITICAL = '\033[31m' # dark red ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' @@ -32,6 +23,7 @@ OKGREEN = '' WARNING = '' FAIL = '' + CRITICAL = '' ENDC = '' BOLD = '' UNDERLINE = '' diff --git a/common/bitbase.py b/common/bitbase.py new file mode 100644 index 000000000..43d7b4b1f --- /dev/null +++ b/common/bitbase.py @@ -0,0 +1,169 @@ +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Basic constants used in multiple modules. + +See also bitlicense.py for additional constants and logic.""" +import os +from enum import Enum +from pathlib import Path + +# |-------------| +# | Application | +# |-------------| + +# Used as a label in the GUI. Not sure if this should be translatable. +APP_NAME = 'Back In Time' + +BINARY_NAME_BASE = 'backintime' +BINARY_NAME_CLI = f'{BINARY_NAME_BASE}' +BINARY_NAME_GUI = f'{BINARY_NAME_BASE}-qt' +PACKAGE_NAME_CLI = f'{BINARY_NAME_BASE}-common' +PACKAGE_NAME_GUI = f'{BINARY_NAME_BASE}-qt' + + +# |-----------------| +# | Several strings | +# |-----------------| + +# Used in about dialog to add language independent translator credits +TRANSLATION_CREDITS_MISC = ( + 'Launchpad translators ', + 'https://www.reddit.com/r/translator', + 'Several mailing lists in Debian (@lists.debian.org) & Ubuntu ' + '(@lists.ubuntu.com) especially the user related lists', +) + + +# |-------------------------------| +# | Online resources & references | +# |-------------------------------| + +# See issue #1734 and #1735 +URL_ENCRYPT_TRANSITION = 'https://github.com/bit-team/backintime' \ + '/blob/-/doc/ENCRYPT_TRANSITION.md' +URL_SOURCE = 'https://github.com/bit-team/backintime' +URL_WEBSITE = URL_SOURCE +URL_FAQ = f'{URL_WEBSITE}/blob/-/FAQ.md' +URL_ISSUES = f'{URL_WEBSITE}/issues' +URL_ISSUES_CREATE_NEW = f'{URL_ISSUES}/new' +URL_CHANGELOG = f'{URL_WEBSITE}/blob/-/CHANGELOG.md' +URL_TRANSLATION = 'https://translate.codeberg.org/engage/backintime' +URL_USER_MANUAL = 'https://backintime.readthedocs.io' + +# |---------------------| +# | Directories & files | +# |---------------------| + +FILENAME_CONFIG = 'config' + +_DIR_DOC_PATH_BASE = Path('/') / 'usr' / 'share' / 'doc' + +USER_MANUAL_LOCAL_PATH = _DIR_DOC_PATH_BASE / PACKAGE_NAME_CLI \ + / 'manual' / 'index.html' +USER_MANUAL_LOCAL_AVAILABLE = USER_MANUAL_LOCAL_PATH.exists() + +CHANGELOG_LOCAL_PATH = _DIR_DOC_PATH_BASE / PACKAGE_NAME_CLI / 'CHANGELOG.html' +CHANGELOG_LOCAL_AVAILABLE = CHANGELOG_LOCAL_PATH.exists() +CHANGELOG_DEBIAN_GZ = _DIR_DOC_PATH_BASE / PACKAGE_NAME_CLI / 'changelog.gz' + +DIR_CALLBACK_EXAMPLES = _DIR_DOC_PATH_BASE / PACKAGE_NAME_CLI \ + / 'user-callback-examples' +DEFAULT_CALLBACK = DIR_CALLBACK_EXAMPLES / 'user-callback.default' + +# Names used for backup directories (or symlinks to them) indicating a specific +# state. +DIR_NAME_LAST_SNAPSHOT = 'last_snapshot' +DIR_NAME_NEWSNAPSHOT = 'new_snapshot' +DIR_NAME_SAVETOCONTINUE = 'save_to_continue' + + +DIR_SSH_KEYS = Path.home() / '.ssh' + +# |-------------------| +# | Enums & constants | +# |-------------------| + +# Used in context of CLI and argument parsing +RETURN_OK = 0 +RETURN_ERR = 1 +RETURN_NO_CFG = 2 + + +class TimeUnit(Enum): + """Describe time units used in context of scheduling.""" + HOUR = 10 # Config.HOUR + DAY = 20 # Config.DAY + WEEK = 30 # Config.WEEK + MONTH = 40 # Config.MONTH + YEAR = 80 # Config.YEAR + + +class ScheduleMode(Enum): + """Describe schedule mode. + + 0 = Disabled + 1 = at every boot + 2 = every 5 minute + 4 = every 10 minute + 7 = every 30 minute + 10 = every hour + 12 = every 2 hours + 14 = every 4 hours + 16 = every 6 hours + 18 = every 12 hours + 19 = custom defined hours + 20 = every day + 25 = daily anacron + 27 = when drive get connected + 30 = every week + 40 = every month + 80 = every year + """ + DISABLED = 0 + AT_EVERY_BOOT = 1 + MINUTES_5 = 2 + MINUTES_10 = 4 + MINUTES_30 = 7 + HOUR = 10 + HOUR_1 = 10 + HOURS_2 = 12 + HOURS_4 = 14 + HOURS_6 = 16 + HOURS_12 = 18 + CUSTOM_HOUR = 19 + DAY = 20 + REPEATEDLY = 25 + UDEV = 27 + WEEK = 30 + MONTH = 40 + YEAR = 80 + + +HOURLY_BACKUPS = ( + ScheduleMode.HOUR, + ScheduleMode.HOUR_1, + ScheduleMode.HOURS_2, + ScheduleMode.HOURS_4, + ScheduleMode.HOURS_6, + ScheduleMode.HOURS_12, + ScheduleMode.CUSTOM_HOUR) + +# |------| +# | Misc | +# |------| + +# Indicator if BIT is running in root mode +IS_IN_ROOT_MODE = os.geteuid() == 0 + +# About transition of encryption feature and the removal of EncFS (see #1734). +# The warnings and deprecation messages are gradually increased in intensity +# and clarity. This constant is the currently desired stage of intensity. The +# last shown intensity is stored in the state data file. If they don't fit, the +# message is displayed. +ENCFS_MSG_STAGE = 3 diff --git a/common/bitlicense.py b/common/bitlicense.py new file mode 100644 index 000000000..53e815bf9 --- /dev/null +++ b/common/bitlicense.py @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: © 2025 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Constants and logic related to license and copyright information. + +That module is separated from bitbase.py because it contain translatable +strings but bitbase is not able to provide it.""" +from pathlib import Path +import bitbase + +COPYRIGHT = 'Copyright © 2008-2024 ' \ + 'Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze\n' \ + 'Copyright © 2022 ' \ + 'Christian Buhtz, Michael Büker, Jürgen Altfeld' + + +URL_GPL_TWO = 'https://spdx.org/licenses/GPL-2.0-or-later.html' + + +def get_gpl_short_text(href: str = None) -> str: + """Short description of primary license. + + The string is used in the AboutDialog and when using --license on shell. + + Dev note (buhtz, 2025-03): That string is untranslated on purpose. + It is legally relevant, and no one should be given the opportunity + to change the string—whether intentionally or accidentally. + """ + + gpl = 'GNU General Public License v2.0 or later (GPL-2.0-or-later)' + + if href: + gpl = f'{gpl}' + + return f'The application is released under {gpl}.' + + +TXT_LICENSES = _( + 'All licenses used in this project are located in the {dir_link} ' + 'directory. To extract per-file license and copyright information ' + 'using SPDX metadata, refer to {readme_link}.') + + +def _determine_licenses_dir() -> str | None: + for pkg in (bitbase.PACKAGE_NAME_GUI, + bitbase.PACKAGE_NAME_CLI, + bitbase.BINARY_NAME_GUI, + bitbase.BINARY_NAME_CLI, + bitbase.BINARY_NAME_BASE): + for path in (Path('/usr/share/doc'), Path('/usr/share/licenses')): + + fp = path / pkg / 'LICENSES' + if fp.is_dir(): + return fp + + # it might be a source repo + fp = Path.cwd().parent / 'LICENSES' + if fp.is_dir(): + return fp + + return None + + +DIR_LICENSES = _determine_licenses_dir() + +FALLBACK_DIR_LICENSES = f'{bitbase.URL_SOURCE}/tree/dev/LICENSES' +FALLBACK_LICENSES_MD = f'{bitbase.URL_SOURCE}/blob/-/LICENSES.md' diff --git a/common/cli.py b/common/cli.py index 4d14acf30..a0be95c3e 100644 --- a/common/cli.py +++ b/common/cli.py @@ -1,62 +1,76 @@ -# -*- coding: utf-8 -*- -# Back In Time -# Copyright (C) 2012-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os import sys - +import atexit import tools +import daemon import snapshots import bcolors +import config +import logger +import bitbase +from typing import Optional +from version import __version__ -def restore(cfg, snapshot_id = None, what = None, where = None, **kwargs): + +def restore(cfg, snapshot_id=None, what=None, where=None, **kwargs): if what is None: what = input('File to restore: ') - what = tools.preparePath(os.path.abspath(os.path.expanduser(what))) + + what = os.path.abspath(os.path.expanduser(what)) if where is None: where = input('Restore to (empty for original path): ') + if where: - where = tools.preparePath(os.path.abspath(os.path.expanduser(where))) + where = os.path.abspath(os.path.expanduser(where)) snapshotsList = snapshots.listSnapshots(cfg) - sid = selectSnapshot(snapshotsList, cfg, snapshot_id, 'SnapshotID to restore') + sid = selectSnapshot( + snapshotsList, cfg, snapshot_id, 'SnapshotID to restore') print('') + RestoreDialog(cfg, sid, what, where, **kwargs).run() -def remove(cfg, snapshot_ids = None, force = None): + +def remove(cfg, snapshot_ids=None, force=None): snapshotsList = snapshots.listSnapshots(cfg) + if not snapshot_ids: snapshot_ids = (None,) - sids = [selectSnapshot(snapshotsList, cfg, sid, 'SnapshotID to remove') for sid in snapshot_ids] + + sids = [ + selectSnapshot(snapshotsList, cfg, sid, 'SnapshotID to remove') + for sid in snapshot_ids + ] if not force: - print('Do you really want to remove this snapshots?') - [print(sid.displayName) for sid in sids] + print('Do you really want to remove these backups?') + + for sid in sids: + print(sid.displayName) + if not 'yes' == input('(no/yes): '): return s = snapshots.Snapshots(cfg) - [s.remove(sid) for sid in sids] -def checkConfig(cfg, crontab = True): + for sid in sids: + s.remove(sid) + + +def checkConfig(cfg, crontab=True): import mount from exceptions import MountException + def announceTest(): print() print(frame(test)) @@ -74,68 +88,93 @@ def errorHandler(msg): mode = cfg.snapshotsMode() if cfg.SNAPSHOT_MODES[mode][0] is not None: - #preMountCheck + # preMountCheck test = 'Run mount tests' announceTest() mnt = mount.Mount(cfg = cfg, tmp_mount = True) + try: mnt.preMountCheck(mode = mode, first_run = True) + except MountException as ex: failed() print(str(ex)) return False + okay() - #okay, lets try to mount + # okay, let's try to mount test = 'Mount' announceTest() + try: - hash_id = mnt.mount(mode = mode, check = False) + hash_id = mnt.mount(mode=mode, check=False) + except MountException as ex: failed() print(str(ex)) return False + okay() - test = 'Check/prepair snapshot path' + test = 'Check/prepare backup path' announceTest() - snapshots_path = cfg.snapshotsPath(mode = mode, tmp_mount = True) + snapshots_mountpoint = cfg.get_snapshots_mountpoint(tmp_mount=True) - if not cfg.setSnapshotsPath(snapshots_path, mode = mode): + ret = tools.validate_and_prepare_snapshots_path( + path=snapshots_mountpoint, + host_user_profile=cfg.hostUserProfile(), + mode=mode, + copy_links=cfg.copyLinks(), + error_handler=cfg.notifyError) + + if not ret: failed() return False + okay() - #umount + # umount if not cfg.SNAPSHOT_MODES[mode][0] is None: test = 'Unmount' announceTest() + try: - mnt.umount(hash_id = hash_id) + mnt.umount(hash_id=hash_id) + except MountException as ex: failed() print(str(ex)) return False + okay() test = 'Check config' announceTest() + if not cfg.checkConfig(): failed() return False + okay() if crontab: test = 'Install crontab' announceTest() - if not cfg.setupCron(): + + try: + cfg.setup_automation() + except Exception as exc: failed() + print(str(exc)) return False + okay() return True -def selectSnapshot(snapshotsList, cfg, snapshot_id = None, msg = 'SnapshotID'): + +def selectSnapshot(snapshotsList, cfg, snapshot_id=None, msg='SnapshotID'): """ check if given snapshot is valid. If not print a list of all snapshots and ask to choose one @@ -143,63 +182,91 @@ def selectSnapshot(snapshotsList, cfg, snapshot_id = None, msg = 'SnapshotID'): len_snapshots = len(snapshotsList) if not snapshot_id is None: + try: sid = snapshots.SID(snapshot_id, cfg) + if sid in snapshotsList: return sid else: print('SnapshotID %s not found.' % snapshot_id) + except ValueError: try: index = int(snapshot_id) return snapshotsList[index] + except (ValueError, IndexError): print('Invalid SnaphotID index: %s' % snapshot_id) + snapshot_id = None columns = (terminalSize()[1] - 25) // 26 + 1 rows = len_snapshots // columns + if len_snapshots % columns > 0: rows += 1 print('SnapshotID\'s:') + for row in range(rows): line = [] + for column in range(columns): index = row + column * rows + if index > len_snapshots - 1: continue - line.append('{i:>4}: {s}'.format(i = index, s = snapshotsList[index])) + + line.append('{i:>4}: {s}'.format(i=index, s=snapshotsList[index])) + print(' '.join(line)) + print('') + while snapshot_id is None: + try: index = int(input(msg + ' (0 - %d): ' % (len_snapshots - 1))) snapshot_id = snapshotsList[index] + except (ValueError, IndexError): print('Invalid Input') continue + return snapshot_id + def terminalSize(): """ get terminal size """ for fd in (sys.stdin, sys.stdout, sys.stderr): + try: - import fcntl, termios, struct - return [int(x) for x in struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))] - except: + import fcntl + import termios + import struct + return [ + int(x) for x in struct.unpack( + 'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + ] + + except ImportError: pass + return [24, 80] -def frame(msg, size = 32): - ret = ' +' + '-' * size + '+\n' + +def frame(msg, size=32): + ret = ' +' + '-' * size + '+\n' ret += ' |' + msg.center(size) + '|\n' - ret += ' +' + '-' * size + '+' + ret += ' +' + '-' * size + '+' + return ret -class RestoreDialog(object): + +class RestoreDialog: def __init__(self, cfg, sid, what, where, **kwargs): self.config = cfg self.sid = sid @@ -208,22 +275,27 @@ def __init__(self, cfg, sid, what, where, **kwargs): self.kwargs = kwargs self.logFile = self.config.restoreLogFile() + if os.path.exists(self.logFile): os.remove(self.logFile) - def callback(self, line, *params): + def callback(self, line, *_args): if not line: return + print(line) - with open(self.logFile, 'a') as log: + + with open(self.logFile, mode='a', encoding='utf-8') as log: log.write(line + '\n') def run(self): s = snapshots.Snapshots(self.config) - s.restore(self.sid, self.what, self.callback, self.where, **self.kwargs) + s.restore( + self.sid, self.what, self.callback, self.where, **self.kwargs) print('\nLog saved to %s' % self.logFile) -class BackupJobDaemon(tools.Daemon): + +class BackupJobDaemon(daemon.Daemon): def __init__(self, func, args): super(BackupJobDaemon, self).__init__() self.func = func @@ -231,3 +303,86 @@ def __init__(self, func, args): def run(self): self.func(self.args, False) + + +def set_quiet(args): + """ + Redirect :py:data:`sys.stdout` to ``/dev/null`` if ``--quiet`` was set on + commandline. Return the original :py:data:`sys.stdout` file object which + can be used to print absolute necessary information. + + Args: + args (argparse.Namespace): + previously parsed arguments + + Returns: + sys.stdout: default sys.stdout + """ + force_stdout = sys.stdout + + if args.quiet: + # do not replace with subprocess.DEVNULL - will not work + sys.stdout = open(os.devnull, 'w') + atexit.register(sys.stdout.close) + atexit.register(force_stdout.close) + + return force_stdout + + +def print_header(): + """Print application name, version and legal notes.""" + print( + f'\n{bitbase.APP_NAME}\n' + f'Version: {__version__}\n' + '\n' + 'Back In Time comes with ABSOLUTELY NO WARRANTY.\n' + 'This is free software, and you are welcome to redistribute it\n' + "under certain conditions; type `backintime --license' for details.\n" + '\n' + ) + + +def get_config_and_select_profile( + config_path: str, + data_path: str, + profile: str, + checksum: Optional[bool] = None, + check: bool = True) -> config.Config: + """Load config and change to profile selected on commandline. + + Args: + config_path: Path to config file. + data_path: Path to "share_path". + profile: Name or ID of the profile. + checksum: Use checksum option. + check: If ``True`` check if config is valid. + + Returns: + Current config with requested profile selected. + + Raises: SystemExit: 1 if ``profile`` or ``profile_id`` is no valid + profile. 2 if ``check`` is ``True`` and config is not configured + + """ + cfg = config.Config( + config_path=config_path, + data_path=data_path) + + if profile: + if profile.isdigit(): + if not cfg.setCurrentProfile(int(profile)): + logger.error(f'Profile-ID not found: {profile}') + sys.exit(bitbase.RETURN_ERR) + else: + if not cfg.setCurrentProfileByName(profile): + logger.error(f'Profile not found: {profile}') + sys.exit(bitbase.RETURN_ERR) + + if check and not cfg.isConfigured(): + logger.error(f'{cfg.APP_NAME} is not configured!') + sys.exit(bitbase.RETURN_NO_CFG) + + if checksum is not None: + cfg.forceUseChecksum = checksum + + return cfg diff --git a/common/cliarguments.py b/common/cliarguments.py new file mode 100644 index 000000000..a9f6afeda --- /dev/null +++ b/common/cliarguments.py @@ -0,0 +1,1069 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2025 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Split from backintime.py +"""Module about CLI argument parsing and parsers. + +A note about conventions: + - Help text starts with lower letter, with the exception of names. + - Help text ends not with a period. + +Example (good): + --help show this help + + +Example (don't): + --help Show this help. + +""" +import sys +import argparse +import json +import re +import tools +# Workaround for situations where startApp() is not invoked. +# E.g. when using --diagnostics and other argparse.Action +tools.initiate_translation(None) +import bitbase # noqa: E402 +import bitlicense # noqa: E402 +import diagnostics # noqa: E402 +import logger # noqa: E402 +import clicommands # noqa: E402 +from argparse import (ArgumentParser, # noqa: E402 + Namespace, + Action, + ) +from pathlib import Path # noqa: E402 +from version import __version__ # noqa: E402 + + +def _license_info() -> tuple[str, str]: + """Collect license info. + + The projects primary license is extracted from SPDX head of the current + file. Additional licenses in use are extracted from the filenames in + LICENSES directory. This info is combined and returned as two strings. + + Returns: + Primary or project license and additional licenses. + + """ + # Extract the SPDX license info from current file. + # interpreted as primary/project license + + # REUSE-IgnoreStart + prim = re.search( + r'SPDX-License-Identifier:\s(:?.*)', + Path(__file__).read_text(encoding='utf-8') + ) + # REUSE-IgnoreEnd + + try: + result = prim.groups()[0] + + except (AttributeError, IndexError): + result = None + + # all used licenses + licenses = None + if bitlicense.DIR_LICENSES: + licenses = [ + f.with_suffix('').name for f in bitlicense.DIR_LICENSES.iterdir()] + + if result: + licenses.remove(result) + licenses = ', '.join(licenses) + + # combine + result = (result, licenses) + + # any errors? + if not result[0]: + result = ( + f'Unable to extract license info from {__file__}', + result[1]) + logger.error(result[0]) + + if not result[1]: + result = ( + result[0], + 'Unable to extract licenses from LICENSES ' + f'directory "{bitlicense.DIR_LICENSES}".') + logger.error(result[1]) + + return result + + +class ParserAgent: + """Create and manage all parsers.""" + + def __init__(self, + app_name: str, + bin_name: str + ): + # Name of the application e.g. "Back In Time" + self.app_name = app_name + + # Name of the binary e.g. "backintime" + self.bin_name = bin_name + + # Mapping the command names to their handler functions + self._cmd_func_dict = { + 'backup': clicommands.backup, + 'check-config': clicommands.check_config, + 'pw-cache': clicommands.pw_cache, + 'remove': clicommands.remove, + 'restore': clicommands.restore, + 'shutdown': clicommands.shutdown, + 'prune': clicommands.prune, + 'show': clicommands.show_backups, + 'unmount': clicommands.unmount, + # Deprecated commands (#2124) + 'decode': clicommands.decode, + 'backup-job': clicommands.backup_job, + 'smart-remove': clicommands.smart_remove, + 'remove-and-do-not-ask-again': + clicommands.remove_and_donot_ask_again, + # See #2120 + 'benchmark-cipher': clicommands.benchmark_cipher, + # See #2130 for this five commands + 'snapshots-path': clicommands.snapshots_path, + 'last-snapshot': clicommands.last_snapshot, + 'last-snapshot-path': clicommands.last_snapshot_path, + 'snapshots-list': clicommands.snapshots_list, + 'snapshots-list-path': clicommands.snapshots_list_path, + } + + # Public parsers indexed by their (command) name + self.parsers = {} + + # Helper + self._command_subparsers = None + + # ??? + self._aliases = [] + + # Used as epilog for command parses + self._reusable_parsers = {} + + # Start creating all parsers, etc + self._create_reusable_parsers() + self._create_main_parser() + self._create_command_parsers() + + def _build_epilog(self): + # Create with "Text ASCII Generator" by "patorjk" + # https://patorjk.com/software/taag + # Font used is "Mini" + logo = '\n'.join([ + r' _ ___ ___', + r' |_) _. _ | | ._ | o ._ _ _ Version:', + rf' |_) (_| (_ |< _|_ | | | | | | | (/_ {__version__}' + ]) + + prj_license, add_licenses = _license_info() + + epi = '\n'.join([ + logo, + '', + f' Project : {bitbase.URL_WEBSITE}', + f' User Manual : {bitbase.URL_USER_MANUAL}', + ' Copyright : see file LICENSES.md', + f' Project License : {prj_license}', + f'Additional Licenses : {add_licenses}', + ]) + + return epi + + def _create_reusable_parsers(self): + self._create_common_parser() + self._create_profile_parser() + self._create_snapshots_only_parser() # deprecated + self._create_rsync_only_parser() + + @property + def main_parser(self) -> ArgumentParser: + """The main parser""" + return self.parsers['main'] + + def _create_main_parser(self): + """Main argument parser""" + parser = ArgumentParser( + prog=self.bin_name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'] + ], + description=f'command-line interface (CLI) for {self.app_name}, ' + 'to create and manage incremental backups', + epilog=self._build_epilog(), + # because of ASCII art in epilog + formatter_class=argparse.RawTextHelpFormatter, + allow_abbrev=False + ) + + self.parsers['main'] = parser + + parser.add_argument( + '-v', '--version', + action='version', + version='%(prog)s ' + __version__, + help="show %(prog)s's version number") + + parser.add_argument( + '--license', + action=ActionPrintLicense, + nargs=0, + help="show %(prog)s's license details") + + parser.add_argument( + '--diagnostics', + action=ActionPrintDiagnostics, + nargs=0, + help='show helpful info (in JSON format) for better support in ' + 'case of issues') + + def _create_common_parser(self) -> ArgumentParser: + """Common arguments used independent from commands""" + + parser = ArgumentParser(add_help=False) + + parser.add_argument( + '--config', + metavar='PATH', + type=str, + action='store', + help='read config from %(metavar)s ' + '(Default: $XDG_CONFIG_HOME/backintime/config)') + + parser.add_argument( + '--share-path', + metavar='PATH', + type=str, + action='store', + # Hide because deprecated (#2125) + help=argparse.SUPPRESS + # help='Write runtime data (locks, messages, log and ' + # 'mountpoints) to %(metavar)s.' + ) + + parser.add_argument( + '--quiet', + action='store_true', + help='be quiet and suppress messages on stdout') + + parser.add_argument( + '--debug', + action='store_true', + default=False, + help='increase verbosity') + + self._reusable_parsers['common'] = parser + + def _create_profile_parser(self): + """Parser used by commands with profile selection involved.""" + + parser = ArgumentParser(add_help=False) + + # Allow only one of "--profile" or "--profile-id" + profile_group = parser.add_mutually_exclusive_group() + + profile_group.add_argument( + '--profile', '-p', + metavar='NAME|ID', + type=str, + action='store', + help='select profile by name or id' + ) + + # Deprecated (#2125) + profile_group.add_argument( + '--profile-id', + metavar='ID', + type=int, + action='store', + help=argparse.SUPPRESS) + + self._reusable_parsers['profile'] = parser + + def _create_snapshots_only_parser(self): + """Arguments used only by commands + - snapshots-path + - snapshots-list-path + - last-snapshot-path + """ + parser = ArgumentParser(add_help=False) + parser.add_argument( + '--keep-mount', + action='store_true', + help="Don't unmount on exit.") + + self._reusable_parsers['snapshots'] = parser + + def _create_rsync_only_parser(self): + """Arguments used only by rsync related commands: + - backup + - restore + """ + parser = ArgumentParser(add_help=False) + parser.add_argument( + '--checksum', + action='store_true', + help='force to use checksum for checking if ' + 'files have been changed') + + self._reusable_parsers['rsync'] = parser + + def _create_cmd_backup(self): + name = 'backup' + nargs = 0 + self._aliases.append((name, nargs)) + self._aliases.append(('b', nargs)) + + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['rsync'], + self._reusable_parsers['common'], + ], + help='create new backup, if scheduled and not on battery', + description='Create a new backup, but only if the profile is ' + 'scheduled and if the machine is not running on ' + 'battery.' + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + parser.add_argument( + '--background', + action='store_true', + default=False, + help='run in background via daemonization', + ) + + self.parsers[name] = parser + + def _create_cmd_backup_job(self): + name = 'backup-job' + nargs = 0 + self._aliases.append((name, nargs)) + + desc = 'Take a new snapshot in background only if the profile is ' \ + 'scheduled and the machine is not on battery. This is used ' \ + 'by cron jobs.' + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['common'], + self._reusable_parsers['profile'], + self._reusable_parsers['rsync'] + ], + help='take new backup in background', + description=desc) + + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_benchmark_ciphier(self): + name = 'benchmark-cipher' + nargs = '?' + self._aliases.append((name, nargs)) + desc = 'Show a benchmark of all ciphers for ssh transfer.' + + parser = self._command_subparsers.add_parser( + name, + help=None, # suppress help output + description=desc) + + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + parser.add_argument( + 'FILE_SIZE', + type=int, + action='store', + default=40, + nargs='?', + help='File size used for benchmark.') + + def _create_cmd_check_config(self): + name = 'check-config' + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['common'], + ], + help='check full configuration', + description='Check configuration of all profiles and ' + 'install crontab entries.' + ) + + parser.add_argument( + '--no-crontab', + action='store_true', + help='Do not install crontab entries.') + + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_decode(self): + name = 'decode' + nargs = '*' + self._aliases.append((name, nargs)) + + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'], + ], + help='decode paths in encrypted profiles', + description="Decode paths with 'encfsctl decode'." + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + parser.add_argument( + 'PATH', + type=str, + action='store', + nargs='*', + help='Decode PATH. If no PATH is specified on command line ' + 'a list of filenames will be read from stdin.') + + self.parsers[name] = parser + + def _create_cmd_last_snapshot(self): + name = 'last-snapshot' + nargs = 0 + self._aliases.append((name, nargs)) + desc = 'Show the ID of the last snapshot.' + + parser = self._command_subparsers.add_parser( + name, + help=None, + description=desc) + + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_last_snapshot_path(self): + name = 'last-snapshot-path' + nargs = 0 + self._aliases.append((name, nargs)) + parser = self._command_subparsers.add_parser( + name, + parents=[self._reusable_parsers['snapshots']], + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_pw_cache(self): + name = 'pw-cache' + nargs = '*' + self._aliases.append((name, nargs)) + + parser = self._command_subparsers.add_parser( + name, + parents=[self._reusable_parsers['common']], + help='control Password Cache', + description='Control Password Cache for non-interactive cronjobs.') + + parser.set_defaults(func=self._cmd_func_dict[name]) + + parser.add_argument( + 'ACTION', + action='store', + choices=['start', 'stop', 'restart', 'reload', 'status'], + nargs='?', + help='command to send to Password Cache daemon') + + self.parsers[name] = parser + + def _create_cmd_remove(self): + name = 'remove' + nargs = '*' + self._aliases.append((name, nargs)) + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'], + ], + help='remove a backup', + description='Remove a backup.') + + parser.set_defaults(func=self._cmd_func_dict[name]) + + parser.add_argument( + 'BACKUP_ID', + type=str, + action='store', + nargs='*', + help='ID of backup to be removed') + + parser.add_argument( + '--skip-confirmation', + action='store_true', + default=False, + help='skip confirmation question; be careful!' + ) + + self.parsers[name] = parser + + def _create_cmd_remove_and_donot_ask_again(self): + name = 'remove-and-do-not-ask-again' + nargs = '*' + self._aliases.append((name, nargs)) + + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['common'], + self._reusable_parsers['profile'] + ], + help=name, # On purpose, because the command name is to long. + # Otherwise print_usage_without_deprecations() won't + # work. + description="Remove backup and don't ask for confirmation " + "before." + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + parser.add_argument( + 'BACKUP_ID', + type=str, + action='store', + nargs='*', + help='ID of snapshots which should be removed.') + + self.parsers[name] = parser + + def _create_cmd_restore(self): + name = 'restore' + nargs = '*' + self._aliases.append((name, nargs)) + + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['rsync'], + self._reusable_parsers['common'], + ], + help='restores backup or files or folders from them', + description='Restores entire backups or selected files and ' + 'folders from them.' + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + backup_group = parser.add_mutually_exclusive_group() + + parser.add_argument( + 'WHAT', + type=str, + action='store', + nargs='?', + help='restore file or directory WHAT') + + parser.add_argument( + 'WHERE', + type=str, + action='store', + nargs='?', + help='restore to WHERE; empty argument will default to ' + 'original destination') + + parser.add_argument( + 'BACKUP_ID', + type=str, + action='store', + nargs='?', + help='specific ID or an integer as index (0=last backup; -1=very ' + 'first backup)') + + parser.add_argument( + '--delete', + action='store_true', + help='Restore and delete newer files which are not in the ' + 'snapshot. WARNING: deleting files in filesystem root could ' + 'break your whole system!!!') + + backup_group.add_argument( + '--local-backup', + action='store_true', + help='Create backup files before changing local files.') + + backup_group.add_argument( + '--no-local-backup', + action='store_true', + help='Temporarily disable creation of backup files before ' + 'changing local files. This can be switched off permanently ' + 'in Settings, too.') + + parser.add_argument( + '--only-new', + action='store_true', + help='Only restore files which do not exist or are newer than ' + 'those in destination. Using "rsync --update" option.') + + self.parsers[name] = parser + + def _create_cmd_shutdown(self): + name = 'shutdown' + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'], + ], + help='shutdown after backup', + description='Shut down the computer after the backup is finished.' + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + self.parsers[name] = parser + + def _create_cmd_smart_remove(self): + name = 'smart-remove' + desc = 'Remove snapshots based on "Smart Removal" pattern.' + + parser = self._command_subparsers.add_parser( + name, + help=desc, + description=desc) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + self.parsers[name] = parser + + def _create_cmd_prune(self): + name = 'prune' + + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'], + ], + help='prune backups based on configured "Remove & ' + 'Retention" rules', + description='Remove and keep backups based on "Remove & ' + 'Retention" policy.' + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + self.parsers[name] = parser + + def _create_cmd_snapshots_list(self): + name = 'snapshots-list' + nargs = 0 + self._aliases.append((name, nargs)) + desc = 'Show a list of snapshot IDs.' + + parser = self._command_subparsers.add_parser( + name, + parents=[self._reusable_parsers['snapshots']], + help=None, + description=desc) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + self.parsers[name] = parser + + def _create_cmd_snapshots_list_path(self): + name = 'snapshots-list-path' + nargs = 0 + self._aliases.append((name, nargs)) + desc = "Show the paths to snapshots." + + parser = self._command_subparsers.add_parser( + name, + parents=[self._reusable_parsers['snapshots']], + help=None, + description=desc) + + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_snapshots_path(self): + name = 'snapshots-path' + nargs = 0 + self._aliases.append((name, nargs)) + desc = 'Show the path where snapshots are stored.' + parser = self._command_subparsers.add_parser( + name, + parents=[self._reusable_parsers['snapshots']], + help=None, # suppress help output + description=desc) + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_show(self): + name = 'show' + + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'], + ], + help='show information about backups', + description="List backup ID's (default) or paths (--path) or " + "just the last (--last)", + allow_abbrev=False + ) + + parser.set_defaults(func=self._cmd_func_dict[name]) + + parser.add_argument( + '--path', + action='store_true', + default=False, + help='list backup paths instead of their ID') + + parser.add_argument( + '--last', + action='store_true', + default=False, + help='show the last (youngest) backup only') + + self.parsers[name] = parser + + def _create_cmd_unmount(self): + name = 'unmount' + nargs = 0 + self._aliases.append((name, nargs)) + parser = self._command_subparsers.add_parser( + name, + parents=[ + self._reusable_parsers['profile'], + self._reusable_parsers['common'], + ], + help='unmount the profile', + description='Unmount the profile.' + ) + parser.set_defaults(func=self._cmd_func_dict[name]) + self.parsers[name] = parser + + def _create_cmd_aliase_switches(self): + # define aliases for all commands with trailing -- + # DEPRECATED and REMOVE it + group = self.parsers['main'].add_mutually_exclusive_group() + + for alias, nargs in self._aliases: + + arg = f'-{alias}' + if len(alias) != 1: + arg = f'-{arg}' + + logger.debug(f'COMANND ALIAS: "{alias}" -> "{arg}"', self) + group.add_argument( + arg, + nargs=nargs, + action=PseudoAliasAction, + # Don't show that alias in help (-h | --help) output + help=argparse.SUPPRESS + ) + + def _create_command_parsers(self): + self._command_subparsers = self.parsers['main'].add_subparsers( + title='Commands', dest='command') + + self._create_cmd_backup() + self._create_cmd_backup_job() + self._create_cmd_show() + self._create_cmd_restore() + self._create_cmd_remove() + self._create_cmd_remove_and_donot_ask_again() + self._create_cmd_prune() + self._create_cmd_smart_remove() + self._create_cmd_unmount() + self._create_cmd_shutdown() + self._create_cmd_benchmark_ciphier() + self._create_cmd_check_config() + self._create_cmd_decode() + self._create_cmd_pw_cache() + self._create_cmd_last_snapshot() + self._create_cmd_last_snapshot_path() + self._create_cmd_snapshots_list() + self._create_cmd_snapshots_list_path() + self._create_cmd_snapshots_path() + + self._create_cmd_aliase_switches() + + +def print_usage_without_deprecations(parser): + """Hidde commands form the parsers help output, print it and exit. + + This is a workaround because argparse can suppress arguments but not + commands (subparsers). The help output contain a online list of + commands. This line is the one that gets manipulated here. + + Commands: + {backup,backup-job,check-config,...,unmount} + + A second location where a command appears is the line-by-line help output. + + Commands: + backup Bla bla + foo bar + + """ + text = parser.format_help().splitlines() + # for idx, t in enumerate(text): + # print(f'{idx=} {t=}') + + deprecated_cmds = [ + 'benchmark-cipher', + 'snapshots-path', + 'last-snapshot', + 'last-snapshot-path', + 'snapshots-list', + 'snapshots-list-path', + 'backup-job', + 'smart-remove', + 'remove-and-do-not-ask-again', + 'decode', + ] + + def _remove_cmds_from_cmd_list(line: str): + """Remove all deprecated commands from that one line like this: + + {backup,backup-job,check-config,...,unmount} + + Usually there are two of this lines in a help-usage-output. + """ + for cmd in deprecated_cmds: + # replace "cmd" between delemiters with "," + pattern = r'(?<=[{,])' + re.escape(cmd) + r'(?=[,}])' + line = re.sub(pattern, ',', line) + # clean up to much "," + line = re.sub(r',+', ',', line) + line = re.sub(r'{,', '{', line) + line = re.sub(r',}', '}', line) + + return line + + rex = re.compile(r'.*{.*}.*') + line_idx_to_remove = [] + + for idx, line in enumerate(text[:]): + # Remove commands from the one-line-list + if rex.match(line): + text[idx] = _remove_cmds_from_cmd_list(line) + continue + + # Line-by-line command description? + for cmd in deprecated_cmds: + pattern = r'\s+' + re.escape(cmd) + r'(?=\s|$)' + if re.match(pattern, line): + line_idx_to_remove.append(idx) + continue + + # remove lines with deprecated commands + for idx in reversed(line_idx_to_remove): + del text[idx] + + print('\n'.join(text)) + sys.exit(0) + + +def parse_arguments(args: Namespace, + agent: ParserAgent) -> Namespace: + """Parse arguments given on commandline. + + Args: + args: Namespace that should be enhanced or ``None``. + + Returns: + New parsed Namespace. + """ + + def join(args, sub_args): + """ + Add new arguments to existing Namespace. + + Args: + args (argparse.Namespace): + main Namespace that should get new arguments + sub_args (argparse.Namespace): + second Namespace which have new arguments + that should be merged into ``args`` + """ + for key, value in vars(sub_args).items(): + # Only add new values if it isn't set already or if there really IS + # a value + if getattr(args, key, None) is None or value: + setattr(args, key, value) + + # First parse the main parser without subparsers + # otherwise positional args in subparsers will be to greedy + # but only if -h or --help is not involved because otherwise + # help will not work for subcommands + main_parser = agent.main_parser + sub = [] + + if '-h' not in sys.argv and '--help' not in sys.argv: + + for i in main_parser._actions: + + if isinstance(i, argparse._SubParsersAction): + # Remove subparsers + main_parser._remove_action(i) + sub.append(i) + + else: + # Manipulate the main parsers output only (not the subparsers) + if sys.argv[1] in ['-h', '--help']: + print_usage_without_deprecations(main_parser) + + args, unknown_args = main_parser.parse_known_args(args) + + # Read subparsers again + for i in sub: + main_parser._add_action(i) + + # Parse it again for unknown args + if unknown_args: + sub_args, unknown_args = main_parser.parse_known_args(unknown_args) + join(args, sub_args) + + # Finally parse only the command parser, otherwise we miss some arguments + # from command + if (unknown_args + and 'command' in args + and args.command in agent.parsers): + cmd_parser = agent.parsers[args.command] + sub_args, unknown_args = cmd_parser.parse_known_args(unknown_args) + join(args, sub_args) + + try: + logger.DEBUG = args.debug + except AttributeError: + pass + + args_dict = vars(args) + used_args = { + key: args_dict[key] + for key + in filter(lambda key: args_dict[key] is not None, args_dict) + } + + logger.debug(f'Argument(s) used: {used_args}') + + # Deprecated (#2125) + if args.profile_id: + clicommands.show_deprecation_message('--profile-id') + args.profile = str(args.profile_id) + + if args.share_path: + clicommands.show_deprecation_message('--share-path') + + # Report unknown arguments but not if we run aliasParser next because we + # will parse again in there. + if unknown_args and not ('func' in args and args.func is alias_parser): + main_parser.error(f'Unknown argument(s): {unknown_args}') + + return args + + +class ActionPrintLicense(argparse.Action): + """Print license details.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __call__(self, *args, **kwargs): + text_gpl = bitlicense.get_gpl_short_text() + text_licenses = bitlicense.TXT_LICENSES.format( + dir_link=bitlicense.DIR_LICENSES, + readme_link=bitlicense.DIR_LICENSES.parent / 'LICENSES.md') + + print(f'{text_gpl}\n{text_licenses}') + + sys.exit(bitbase.RETURN_OK) + + +class ActionPrintDiagnostics(argparse.Action): + """See `collect_diagnostics()` for details.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __call__(self, *args, **kwargs): + data = diagnostics.collect_diagnostics() + print(json.dumps(data, indent=4)) + sys.exit(bitbase.RETURN_OK) + + +def alias_parser(args: Namespace): + """Call commands which where given with leading -- for backwards + compatibility. + + Args: + args: Previously parsed arguments + """ + + if not args.quiet: + logger.info(f"Run command '{args.alias}' instead of argument " + f"'{args.replace}' due to backwards compatibility.") + + msg = ( + f'The command alias "{args.replace}" is deprecated and will be ' + 'removed from Back In Time in the foreseeable future, without any ' + 'replacement.') + # ToDo: Switch this later to ERROR + logger.warning(msg) + + argv = [w.replace(args.replace, args.alias) for w in sys.argv[1:]] + + new_args = parse_arguments( + argv, + agent=ParserAgent(bitbase.APP_NAME, 'backintime') + ) + + if 'func' in dir(new_args): + new_args.func(new_args) + + +class PseudoAliasAction(Action): + """Translate '--COMMAND' into 'COMMAND' for backwards compatibility. + """ + + def __call__(self, parser, namespace, values, option_string=None): + """ + Args: + parser (argparse.ArgumentParser): NotImplemented + namespace (argparse.Namespace): Namespace that should get modified + values: NotImplemented + option_string: NotImplemented + """ + dest = self.dest.replace('_', '-') + + if self.dest == 'b': + replace = '-b' + alias = 'backup' + + else: + replace = f'--{dest}' + alias = dest + + setattr(namespace, 'func', alias_parser) + setattr(namespace, 'replace', replace) + setattr(namespace, 'alias', alias) diff --git a/common/clicommands.py b/common/clicommands.py new file mode 100644 index 000000000..0e2526281 --- /dev/null +++ b/common/clicommands.py @@ -0,0 +1,693 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2025 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Split from backintime.py +"""Module about CLI commands""" +import sys +import argparse +from datetime import datetime +from time import sleep +import tools +# Workaround for situations where startApp() is not invoked. +# E.g. when using --diagnostics and other argparse.Action +tools.initiate_translation(None) +import logger +import snapshots +import sshtools +import password +import encfstools +import cli +import config +import bitbase +import mount +from exceptions import MountException +from applicationinstance import ApplicationInstance +from shutdownagent import ShutdownAgent + + +def _deprecation_msg(cmd_flag: str, replacement: str) -> str: + if not replacement: + replacement = 'A replacement is not planned.' + + kind = 'flag' if cmd_flag[0] == '-' else 'command' + + return ( + f'The {kind} "{cmd_flag}" is deprecated and will be removed from Back ' + f'In Time in the foreseeable future. {replacement} Feel free to ' + 'contact the project team if you have any questions or suggestions.') + + +def show_deprecation_message(cmd_flag: str): + """Centralize management of deprecation message regarding CLI commands and + flags. + + As an exception the deprecation messages for flag-aliases (e.g. '--backup' + for 'backup') are managed in `cliargument.alias_parser()`. + """ + + # 'None' means no replacement planned. + replacement = { + 'benchmark-cipher': None, + 'snapshots-path': None, + 'snapshots-list': 'Use "show" instead.', + 'snapshots-list-path': 'Use "show --path" instead.', + 'last-snapshot': 'Use "show --last" instead.', + 'last-snapshot-path': 'Use "show --last --path" instead.', + 'backup-job': 'Use "backup --background" instead.', + 'smart-remove': 'Use "prune" instead.', + 'remove-and-do-not-ask-again': + 'Use "remove --skip-confirmation" instead.', + '--profile-id': 'Use "--profile" instead.', + '--share-path': None, + 'decode': None, + }[cmd_flag] + + msg = _deprecation_msg(cmd_flag, replacement) + + # ToDo: Switch this later to ERROR + logger.warning(msg) + + +def _get_config(args: argparse.Namespace) -> config.Config: + """A dirty little helper. Feel free to refactor.""" + return cli.get_config_and_select_profile( + config_path=args.config, + data_path=args.share_path, + profile=args.profile, + checksum=getattr(args, 'checksum', None) + ) + + +def backup(args: argparse.Namespace, force: bool = True): + """ + Command for force taking a new snapshot. + + Args: + args (argparse.Namespace): + previously parsed arguments + force (bool): take the snapshot even if it wouldn't need to or would + be prevented (e.g. running on battery) + + Raises: + SystemExit: 0 if successful, 1 if not + """ + + # Run backup in background? + if args.background: + # "Force" will be False + cli.BackupJobDaemon(_do_backup, args).start() + else: + _do_backup(args, force) + + +def _do_backup(args: argparse.Namespace, force: bool): + """ + Command for force taking a new snapshot. + + Args: + args (argparse.Namespace): + previously parsed arguments + force (bool): take the snapshot even if it wouldn't need to or would + be prevented (e.g. running on battery) + + Raises: + SystemExit: 0 if successful, 1 if not + """ + + cli.set_quiet(args) + cli.print_header() + cfg = _get_config(args) + + tools.envLoad(cfg.cronEnvFile()) + ret = snapshots.Snapshots(cfg).backup(force) + + sys.exit(int(ret)) + + +def backup_job(args: argparse.Namespace): + """ + Command for taking a new snapshot in background. Mainly used for cronjobs. + This will run the snapshot inside a daemon and detach from it. It will + return immediately back to commandline. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 + """ + show_deprecation_message('backup-job') + args.background = True + backup(args) + + +def benchmark_cipher(args: argparse.Namespace): + """ + Command for transferring a file with scp to remote host with all + available ciphers and print its speed and time. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + show_deprecation_message('benchmark-cipher') + + cli.set_quiet(args) + cli.print_header() + + cfg = _get_config(args) + + if cfg.snapshotsMode() in ('ssh', 'ssh_encfs'): + ssh = sshtools.SSH(cfg) + ssh.benchmarkCipher(args.FILE_SIZE) + sys.exit(bitbase.RETURN_OK) + + # else + logger.error( + f"SSH is not configured for profile '{cfg.profileName()}'!") + sys.exit(bitbase.RETURN_ERR) + + +def check_config(args: argparse.Namespace): + """Check the config file. + + In case of no errors application exists with 0, otherwise 1. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 if config is okay, 1 if not. + + """ + force_stdout = cli.set_quiet(args) + cli.print_header() + cfg = _get_config(args) + + msg = f'\nConfig {cfg._LOCAL_CONFIG_PATH} profile ' \ + f"'{cfg.profileName()}'" + + if cli.checkConfig(cfg, crontab=not args.no_crontab): + print(f'{msg} is fine.', file=force_stdout) + sys.exit(bitbase.RETURN_OK) + + # else + print(f'{msg} has errors.', file=force_stdout) + sys.exit(bitbase.RETURN_ERR) + + +def decode(args: argparse.Namespace): + """Decoding paths given paths with 'encfsctl'. + + Will listen on stdin if no path was given. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 + """ + show_deprecation_message('decode') + force_stdout = cli.set_quiet(args) + cfg = _get_config(args) + + if cfg.snapshotsMode() not in ('local_encfs', 'ssh_encfs'): + logger.error(f"Profile '{cfg.profileName()}' is not encrypted.") + sys.exit(bitbase.RETURN_ERR) + + _mount(cfg) + decoder = encfstools.Decode(cfg) + + if not args.PATH: + + while True: + + try: + path = input() + except EOFError: + break + + if not path: + break + + print(decoder.path(path), file=force_stdout) + + else: + print('\n'.join(decoder.list(args.PATH)), file=force_stdout) + + decoder.close() + _umount(cfg) + + sys.exit(bitbase.RETURN_OK) + + +def _last_snapshot_base(args: argparse.Namespace, path_info: bool): + """Print info about the very last (youngest) snapshot in current profile. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 + """ + force_stdout = cli.set_quiet(args) + cfg = _get_config(args) + _mount(cfg) + sid = snapshots.lastSnapshot(cfg) + + if sid: + # Path or ID + label = 'SnapshotPath' if path_info else 'SnapshotID' + data = sid.path() if path_info else sid + + msg = f'{data}' if args.quiet else f'{label}: {data}' + print(msg, file=force_stdout) + + else: + logger.error(f"There are no snapshots in '{cfg.profileName()}'") + + if not getattr(args, 'keep_mount', None): + _umount(cfg) + + sys.exit(bitbase.RETURN_OK) + + +def last_snapshot(args: argparse.Namespace): + """Print the very last (youngest) snapshot in current profile. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 + """ + show_deprecation_message('last-snapshot') + _last_snapshot_base(args=args, path_info=False) + + +def last_snapshot_path(args: argparse.Namespace): + """Print the path of the very last (youngest) snapshot in + current profile. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + show_deprecation_message('last-snapshot-path') + _last_snapshot_base(args=args, path_info=True) + + +def pw_cache(args: argparse.Namespace): + """Startpassword cache daemon. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 if daemon is running, 1 if not. + """ + force_stdout = cli.set_quiet(args) + cli.print_header() + + cfg = _get_config(args) + ret = bitbase.RETURN_OK + + daemon = password.Password_Cache(cfg) + + if args.ACTION and args.ACTION != 'status': + # call action method + getattr(daemon, args.ACTION)() + + elif args.ACTION == 'status': + + print(f'{cfg.APP_NAME} Password Cache: ', end=' ', file=force_stdout) + + if daemon.status(): + print(f'{cli.bcolors.OKGREEN}running{cli.bcolors.ENDC}', + file=force_stdout) + ret = bitbase.RETURN_OK + + else: + print(f'{cli.bcolors.FAIL}not running{cli.bcolors.ENDC}', + file=force_stdout) + ret = bitbase.RETURN_ERR + + else: + daemon.run() + + sys.exit(ret) + + +def remove(args: argparse.Namespace): + """Remove snapshots. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + cli.set_quiet(args) + cli.print_header() + + cfg = _get_config(args) + _mount(cfg) + + cli.remove( + cfg=cfg, + snapshot_ids=args.BACKUP_ID, + force=args.skip_confirmation) + + _umount(cfg) + + sys.exit(bitbase.RETURN_OK) + + +def remove_and_donot_ask_again(args): + """Removing snapshots without asking (BE CAREFUL!). + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + show_deprecation_message('remove-and-do-not-ask-again') + args.skip_confirmation = True + remove(args=args) + + +def restore(args: argparse.Namespace): + """Restore files from snapshots. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + cli.set_quiet(args) + cli.print_header() + cfg = _get_config(args) + _mount(cfg) + + if cfg.backupOnRestore() and not args.no_local_backup: + isbackup = True + else: + isbackup = args.local_backup + + cli.restore(cfg, + args.BACKUP_ID, + args.WHAT, + args.WHERE, + delete=args.delete, + backup=isbackup, + only_new=args.only_new) + + _umount(cfg) + + sys.exit(bitbase.RETURN_OK) + + +def shutdown(args: argparse.Namespace): + """Shut down the computer after the current snapshot has + finished. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 if successful; 1 if it failed either because there is no + active snapshot for this profile or shutdown is not supported. + + """ + cli.set_quiet(args) + cli.print_header() + cfg = _get_config(args) + + sd = ShutdownAgent() + + if not sd.can_shutdown(): + logger.warning('Shutdown is not supported.') + sys.exit(bitbase.RETURN_ERR) + + instance = ApplicationInstance(cfg.takeSnapshotInstanceFile(), False) + profile = '='.join((cfg.currentProfile(), cfg.profileName())) + + if not instance.busy(): + logger.info('Skip shutdown because there is no active bacukp ' + f'for profile {profile}.') + sys.exit(bitbase.RETURN_ERR) + + print(f'Shutdown is waiting for the running backup in profile {profile} ' + 'to end.\nPress CTRL+C to interrupt shutdown.\n') + sd.activate_shutdown = True + + try: + while instance.busy(): + logger.debug('Backup is still active. Wait for shutdown.') + sleep(5) + + except KeyboardInterrupt: + print('Shutdown interrupted.') + + else: + logger.info('Shuting down now.') + sd.shutdown() + + sys.exit(bitbase.RETURN_OK) + + +def snapshots_path(args: argparse.Namespace): + """Print the full snapshot path of current profile. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + show_deprecation_message('snapshots-path') + + force_stdout = cli.set_quiet(args) + cfg = _get_config(args) + + if args.keep_mount: + _mount(cfg) + + msg = '{}' if args.quiet else 'SnapshotsPath: {}' + print(msg.format(cfg.snapshotsFullPath()), file=force_stdout) + + sys.exit(bitbase.RETURN_OK) + + +def _snapshots_list_base(args: argparse.Namespace, path_info: bool): + """Print infos about a list of all snapshots in current profile. + + Args: + args: Ppreviously parsed arguments + + Raises: + SystemExit: 0 + """ + force_stdout = cli.set_quiet(args) + cfg = _get_config(args) + _mount(cfg) + + if path_info: + msg = '{}' if args.quiet else 'SnapshotPath: {}' + else: + msg = '{}' if args.quiet else 'SnapshotID: {}' + + # Use snapshots.listSnapshots instead of iterSnapshots because of sorting + if path_info: + data = [ + sid.path() for sid in snapshots.listSnapshots(cfg, reverse=False)] + else: + data = list(snapshots.listSnapshots(cfg, reverse=False)) + + for sid_info in data: + print(msg.format(sid_info), file=force_stdout) + + if not data: + logger.error(f"There are no snapshots in '{cfg.profileName()}'") + + if not args.keep_mount: + _umount(cfg) + + sys.exit(bitbase.RETURN_OK) + + +def snapshots_list(args: argparse.Namespace): + """Print a list of all snapshots in current profile. + + Args: + args: Ppreviously parsed arguments + + Raises: + SystemExit: 0 + """ + show_deprecation_message('snapshots-list') + _snapshots_list_base(args=args, path_info=False) + + +def snapshots_list_path(args: argparse.Namespace): + """Print a list of all snapshots paths in current profile. + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 + """ + show_deprecation_message('snapshots-list-path') + _snapshots_list_base(args=args, path_info=True) + + +def show_backups(args: argparse.Namespace): + """Command 'show'. + + Args: + args: Parsed command-line arguments. + + Raises: + SystemExit: With errors or no backups available + `bitbase.RETURN_ERR` (1), otherwise `bitbase.RETURN_OK' (0). + """ + + cfg = _get_config(args) + _mount(cfg) + + # raw data + backups = snapshots.get_backup_ids_and_paths( + cfg=cfg, descending=True, include_new=False) + + if args.last: + backups = backups[-1:] + + if args.path: + # Path + def _element(e): + return str(e[1]) + else: + # ID + def _element(e): + return e[0] + + # one line for each ID/Path + result = '\n'.join( + map(_element, backups) + ) + + print(result) + _umount(cfg) + + if not backups: + logger.error(f'No backups in profile "{cfg.profileName()}"') + sys.exit(bitbase.RETURN_ERR) + + sys.exit(bitbase.RETURN_OK) + + +def smart_remove(args: argparse.Namespace): + show_deprecation_message('smart-remove') + prune(args) + + +def prune(args: argparse.Namespace): + """Run Remove & Retention (aka Smart-Removal). + + Args: + args: Previously parsed arguments. + + Raises: + SystemExit: 0 if okay. 2 if Remove & Retention is not configured. + """ + cli.set_quiet(args) + cli.print_header() + cfg = _get_config(args) + sn = snapshots.Snapshots(cfg) + + enabled, \ + keep_all, \ + keep_one_per_day, \ + keep_one_per_week, \ + keep_one_per_month = cfg.smartRemove() + + if enabled: + _mount(cfg) + del_snapshots = sn.smartRemoveList(datetime.today(), + keep_all, + keep_one_per_day, + keep_one_per_week, + keep_one_per_month) + logger.info(f'{len(del_snapshots)} backups are marked for removal.') + sn.smartRemove(del_snapshots, log=logger.info) + _umount(cfg) + sys.exit(bitbase.RETURN_OK) + + # else + logger.error('Remove & Retention is not configured.') + sys.exit(bitbase.RETURN_NO_CFG) + + +def unmount(args): + """Unmount all filesystems. + + Args: + args: Previously parsed arguments + + Raises: + SystemExit: 0 + """ + cli.set_quiet(args) + + cfg = _get_config(args) + + _mount(cfg) + _umount(cfg) + + sys.exit(bitbase.RETURN_OK) + + +def _mount(cfg: config.Config): + """Mount external filesystems of current selected profile. + + Args: + cfg: Config to identify the current profile. + """ + try: + hash_id = mount.Mount(cfg=cfg).mount() + + except MountException as ex: + logger.error(str(ex)) + sys.exit(bitbase.RETURN_ERR) + + else: + cfg.setCurrentHashId(hash_id) + + +def _umount(cfg: config.Config): + """Unmount external filesystems of current selected profile. + + Args: + cfg: Config to identify the current profile. + """ + try: + mount.Mount(cfg=cfg).umount(cfg.current_hash_id) + + except MountException as ex: + logger.error(str(ex)) diff --git a/common/config-example-local b/common/config-example-local index 6e6d62876..a75b4ba88 100644 --- a/common/config-example-local +++ b/common/config-example-local @@ -54,4 +54,6 @@ profile1.snapshots.smart_remove.keep_one_per_month=24 profile1.snapshots.smart_remove.keep_one_per_week=4 profile1.snapshots.use_checksum=false profile1.snapshots.user_backup.ionice=false +profile1.snapshots.warn_free_space.unit=10 +profile1.snapshots.warn_free_space.value=10240 profiles.version=1 diff --git a/common/config-example-ssh b/common/config-example-ssh index b2f0ebb52..42bc6f2c3 100644 --- a/common/config-example-ssh +++ b/common/config-example-ssh @@ -62,4 +62,6 @@ profile1.snapshots.ssh.private_key_file=/home/USER/.ssh/id_dsa profile1.snapshots.ssh.user=USER profile1.snapshots.use_checksum=false profile1.snapshots.user_backup.ionice=false +profile1.snapshots.warn_free_space.unit=10 +profile1.snapshots.warn_free_space.value=10240 profiles.version=1 diff --git a/common/config.py b/common/config.py index 77eb4c211..e902f512f 100644 --- a/common/config.py +++ b/common/config.py @@ -1,162 +1,114 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Configuration handling and logic. + +This module and its `Config` class contain the application logic handling the +configuration of Back In Time. The handling of the configuration file itself +is separated in the module :py:mod:`configfile`. + +Development notes: + Some of the methods have code comments starting with `#? ` instead of + `# `. These special comments are used to generate the manpage + `backintime-config`. The script `create-manpage-backintime-config.py` + parses this module for that. +""" import os import sys import datetime -import gettext import socket import random +import getpass import shlex +# Workaround: Mostly relevant on TravisCI but not exclusively. +# While unittesting and without regular invocation of BIT the GNU gettext +# class-based API isn't setup yet. +# The bigger problem with config.py is that it do use translatable strings. +# Strings like this do not belong into a config file or its context. try: - import pwd -except ImportError: - import getpass - pwd = None + _('Warning') +except NameError: + _ = lambda val: val +import bitbase import tools import configfile +import encode import logger -import mount import sshtools import encfstools +import gocryptfstools import password import pluginmanager -from exceptions import PermissionDeniedByPolicy, InvalidChar, InvalidCmd, LimitExceeded - -_=gettext.gettext - -gettext.bindtextdomain('backintime', os.path.join(tools.sharePath(), 'locale')) -gettext.textdomain('backintime') +import schedule +from storagesize import StorageSize, SizeUnit +from exceptions import PermissionDeniedByPolicy class Config(configfile.ConfigFileWithProfiles): - APP_NAME = 'Back In Time' - VERSION = '1.3.1' - COPYRIGHT = 'Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze' - CONFIG_VERSION = 6 + APP_NAME = bitbase.APP_NAME - NONE = 0 - AT_EVERY_BOOT = 1 - _5_MIN = 2 - _10_MIN = 4 - _30_MIN = 7 - HOUR = 10 - _1_HOUR = 10 - _2_HOURS = 12 - _4_HOURS = 14 - _6_HOURS = 16 - _12_HOURS = 18 - CUSTOM_HOUR = 19 - DAY = 20 - REPEATEDLY = 25 - UDEV = 27 - WEEK = 30 - MONTH = 40 - YEAR = 80 - - DISK_UNIT_MB = 10 - DISK_UNIT_GB = 20 - - SCHEDULE_MODES = { - NONE : _('Disabled'), - AT_EVERY_BOOT : _('At every boot/reboot'), - _5_MIN: _('Every 5 minutes'), - _10_MIN: _('Every 10 minutes'), - _30_MIN: _('Every 30 minutes'), - _1_HOUR : _('Every hour'), - _2_HOURS : _('Every 2 hours'), - _4_HOURS : _('Every 4 hours'), - _6_HOURS : _('Every 6 hours'), - _12_HOURS : _('Every 12 hours'), - CUSTOM_HOUR : _('Custom Hours'), - DAY : _('Every Day'), - REPEATEDLY : _('Repeatedly (anacron)'), - UDEV : _('When drive get connected (udev)'), - WEEK : _('Every Week'), - MONTH : _('Every Month'), - YEAR : _('Every Year') - } - - REMOVE_OLD_BACKUP_UNITS = { - DAY : _('Day(s)'), - WEEK : _('Week(s)'), - YEAR : _('Year(s)') - } - - REPEATEDLY_UNITS = { - HOUR : _('Hour(s)'), - DAY : _('Day(s)'), - WEEK : _('Week(s)'), - MONTH : _('Month(s)') - } - - MIN_FREE_SPACE_UNITS = { DISK_UNIT_MB : 'MiB', DISK_UNIT_GB : 'GiB' } - - DEFAULT_EXCLUDE = [ '.gvfs', '.cache/*', '.thumbnails*', - '.local/share/[Tt]rash*', '*.backup*', '*~', '.dropbox*', '/proc/*', - '/sys/*', '/dev/*', '/run/*', '/etc/mtab', '/var/cache/apt/archives/*.deb', - 'lost+found/*', '/tmp/*', '/var/tmp/*', '/var/backups/*', '.Private' ] - - DEFAULT_RUN_NICE_FROM_CRON = True - DEFAULT_RUN_NICE_ON_REMOTE = False + CONFIG_VERSION = 6 + """Latest or highest possible version of Back in Time's config file.""" + + NONE = bitbase.ScheduleMode.DISABLED + AT_EVERY_BOOT = bitbase.ScheduleMode.AT_EVERY_BOOT + _5_MIN = bitbase.ScheduleMode.MINUTES_5 + _10_MIN = bitbase.ScheduleMode.MINUTES_10 + _30_MIN = bitbase.ScheduleMode.MINUTES_30 + HOUR = bitbase.ScheduleMode.HOUR + _1_HOUR = bitbase.ScheduleMode.HOUR_1 + _2_HOURS = bitbase.ScheduleMode.HOURS_2 + _4_HOURS = bitbase.ScheduleMode.HOURS_4 + _6_HOURS = bitbase.ScheduleMode.HOURS_6 + _12_HOURS = bitbase.ScheduleMode.HOURS_12 + CUSTOM_HOUR = bitbase.ScheduleMode.CUSTOM_HOUR + DAY = bitbase.ScheduleMode.DAY + REPEATEDLY = bitbase.ScheduleMode.REPEATEDLY + UDEV = bitbase.ScheduleMode.UDEV + WEEK = bitbase.ScheduleMode.WEEK + MONTH = bitbase.ScheduleMode.MONTH + YEAR = bitbase.ScheduleMode.YEAR + + HOURLY_BACKUPS = bitbase.HOURLY_BACKUPS + + DEFAULT_RUN_NICE_FROM_CRON = True + DEFAULT_RUN_NICE_ON_REMOTE = False DEFAULT_RUN_IONICE_FROM_CRON = True DEFAULT_RUN_IONICE_FROM_USER = False DEFAULT_RUN_IONICE_ON_REMOTE = False - DEFAULT_RUN_NOCACHE_ON_LOCAL = False + DEFAULT_RUN_NOCACHE_ON_LOCAL = False DEFAULT_RUN_NOCACHE_ON_REMOTE = False - DEFAULT_SSH_PREFIX = 'PATH=/opt/bin:/opt/sbin:\$PATH' + DEFAULT_SSH_PREFIX = 'PATH=/opt/bin:/opt/sbin:\\$PATH' DEFAULT_REDIRECT_STDOUT_IN_CRON = True DEFAULT_REDIRECT_STDERR_IN_CRON = False + DEFAULT_OFFSET = 0 - exp = _(' EXPERIMENTAL!') - SNAPSHOT_MODES = { - #mode : (, 'ComboBox Text', need_pw|lbl_pw_1, need_2_pw|lbl_pw_2), - 'local' : (None, _('Local'), False, False), - 'ssh' : (sshtools.SSH, _('SSH'), _('SSH private key'), False), - 'local_encfs' : (encfstools.EncFS_mount, _('Local encrypted'), _('Encryption'), False), - 'ssh_encfs' : (encfstools.EncFS_SSH, _('SSH encrypted'), _('SSH private key'), _('Encryption')) - } - - SSH_CIPHERS = {'default': _('Default'), - 'aes128-ctr': _('AES128-CTR'), - 'aes192-ctr': _('AES192-CTR'), - 'aes256-ctr': _('AES256-CTR'), - 'arcfour256': _('ARCFOUR256'), - 'arcfour128': _('ARCFOUR128'), - 'aes128-cbc': _('AES128-CBC'), - '3des-cbc': _('3DES-CBC'), - 'blowfish-cbc': _('Blowfish-CBC'), - 'cast128-cbc': _('Cast128-CBC'), - 'aes192-cbc': _('AES192-CBC'), - 'aes256-cbc': _('AES256-CBC'), - 'arcfour': _('ARCFOUR') } - - ENCODE = encfstools.Bounce() + ENCODE = encode.Bounce() PLUGIN_MANAGER = pluginmanager.PluginManager() - def __init__(self, config_path = None, data_path = None): + def __init__(self, config_path=None, data_path=None): + """Back In Time configuration (and much more then this). + + Args: + config_path (str): Full path to the config file + (default: `~/.config/backintime/config`). + data_path (str): It is $XDG_DATA_HOME (default: `~/.local/share`). + """ + # Note: The main profiles name here is translated using the systems + # current locale because the language code in the config file wasn't + # read yet. configfile.ConfigFileWithProfiles.__init__(self, _('Main profile')) - self._APP_PATH = tools.backintimePath() - self._DOC_PATH = os.path.join(tools.sharePath(), 'doc', 'backintime-common') - if os.path.exists(os.path.join(self._APP_PATH, 'LICENSE')): - self._DOC_PATH = self._APP_PATH + self._unsaved_profiles = [] self._GLOBAL_CONFIG_PATH = '/etc/backintime/config' @@ -182,57 +134,34 @@ def __init__(self, config_path = None, data_path = None): tools.makeDirs(self._LOCAL_MOUNT_ROOT) self._DEFAULT_CONFIG_PATH = os.path.join(self._LOCAL_CONFIG_FOLDER, 'config') + if config_path is None: self._LOCAL_CONFIG_PATH = self._DEFAULT_CONFIG_PATH else: self._LOCAL_CONFIG_PATH = os.path.abspath(config_path) self._LOCAL_CONFIG_FOLDER = os.path.dirname(self._LOCAL_CONFIG_PATH) - old_path = os.path.join(self._LOCAL_CONFIG_FOLDER, 'config2') - - if os.path.exists(old_path): - if os.path.exists(self._LOCAL_CONFIG_PATH): - os.remove(old_path) - else: - os.rename(old_path, self._LOCAL_CONFIG_PATH) + # Load global config file self.load(self._GLOBAL_CONFIG_PATH) + + # Append local config file self.append(self._LOCAL_CONFIG_PATH) - #?Internal version of current config;;self.CONFIG_VERSION - currentConfigVersion = self.intValue('config.version', 5) - if currentConfigVersion < self.CONFIG_VERSION: - # config.version value wasn't stored since BiT version 0.9.99.22 - # until version 1.2.0 because of a bug. So we can't really tell - # which version the config is. But most likely it is version > 4 - if currentConfigVersion < 4: - #update from BackInTime version < 1.0 is deprecated - logger.error("config.version is < 4. This config was made with "\ - "BackInTime version < 1.0. This version ({}) " \ - "doesn't support upgrading config from version " \ - "< 1.0 anymore. Please use BackInTime version " \ - "<= 1.1.12 to upgrade the config to a more recent "\ - "version.".format(self.VERSION)) - #TODO: add popup warning - sys.exit(2) + # Get the version of the config file + # or assume the highest config version if it isn't set. + currentConfigVersion \ + = self.intValue('config.version', self.CONFIG_VERSION) + if currentConfigVersion < self.CONFIG_VERSION: if currentConfigVersion < 5: - logger.info("Update to config version 5: other snapshot locations", self) - profiles = self.profiles() - for profile_id in profiles: - #change include - old_values = self.includeV4(profile_id) - values = [] - for value in old_values: - values.append((value, 0)) - self.setInclude(values, profile_id) - - #change exclude - old_values = self.excludeV4(profile_id) - self.setExclude(old_values, profile_id) - - #remove keys - self.removeProfileKey('snapshots.include_folders', profile_id) - self.removeProfileKey('snapshots.exclude_patterns', profile_id) + logger.error( + 'The config file version is 4 or lower. This config was ' + 'made with a version of Back In Time that is out dated. ' + 'Because of that upgrading config to the current version ' + 'is not possible. The latest Back In Time version ' + 'supporting upgrade the config file was v1.5.2.', + self) + sys.exit(2) if currentConfigVersion < 6: logger.info('Update to config version 6', self) @@ -270,18 +199,91 @@ def __init__(self, config_path = None, data_path = None): # remove old gnome and kde keys self.removeKeysStartsWith('gnome') self.removeKeysStartsWith('kde') + self.save() self.current_hash_id = 'local' self.pw = None self.forceUseChecksum = False - self.xWindowId = None - self.inhibitCookie = None self.setupUdev = tools.SetupUdev() + language_used = tools.initiate_translation(self.language()) + + # Development note (2023-08 by buhtz): + # Not the best location for a variable like this. + self.language_used = language_used + """ISO-639 language code of the used language. See + `tools._determine_current_used_language_code()` for details.""" + + # Workaround + self.default_profile_name = _('Main profile') + + # ToDo Those hidden labels exist to speed up their translation. + # See: https://github.com/bit-team/backintime/issues/ + # 1735#issuecomment-2197646518 + _HIDDEN_NEW_MODE_LABELS = ( + _('Local (EncFS encrypted)'), + _('SSH (EncFS encrypted)') + ) + + self.SNAPSHOT_MODES = { + # mode: ( + # , + # 'ComboBox Text', + # need_pw|lbl_pw_1, + # need_2_pw|lbl_pw_2 + # ), + 'local': ( + None, _('Local'), False, False), + 'local_gocryptfs': ( + gocryptfstools.GocryptfsMount, + _('Local encrypted') + ' (via gocryptfs)', + _('Encryption'), + False + ), + 'ssh': ( + sshtools.SSH, _('SSH'), _('SSH private key'), False), + 'local_encfs': ( + encfstools.EncFS_mount, + 'DEPRECATED - Local encrypted (via EncFS)', + _('Encryption'), + False + ), + 'ssh_encfs': ( + encfstools.EncFS_SSH, + 'DEPRECATED - SSH encrypted (via EncFS)', + _('SSH private key'), + _('Encryption') + ), + } + + # Deprecated: #2176 + self.SSH_CIPHERS = { + 'default': 'Default', + 'aes128-ctr': 'AES128-CTR', + 'aes192-ctr': 'AES192-CTR', + 'aes256-ctr': 'AES256-CTR', + 'arcfour256': 'ARCFOUR256', + 'arcfour128': 'ARCFOUR128', + 'aes128-cbc': 'AES128-CBC', + '3des-cbc': '3DES-CBC', + 'blowfish-cbc': 'Blowfish-CBC', + 'cast128-cbc': 'Cast128-CBC', + 'aes192-cbc': 'AES192-CBC', + 'aes256-cbc': 'AES256-CBC', + 'arcfour': 'ARCFOUR' + } + def save(self): + self._unsaved_profiles = [] self.setIntValue('config.version', self.CONFIG_VERSION) - return super(Config, self).save(self._LOCAL_CONFIG_PATH) + return super().save(self._LOCAL_CONFIG_PATH) + + def is_profile_unsaved(self, profile_id: str) -> bool: + return profile_id in self._unsaved_profiles + + def is_current_profile_unsaved(self) -> bool: + return self.is_profile_unsaved(self.currentProfile()) def checkConfig(self): profiles = self.profiles() @@ -289,18 +291,30 @@ def checkConfig(self): for profile_id in profiles: profile_name = self.profileName(profile_id) snapshots_path = self.snapshotsPath(profile_id) - logger.debug('Check profile %s' %profile_name, self) + logger.debug(f'Check profile {profile_name}', self) - #check snapshots path + # check snapshots path if not snapshots_path: - self.notifyError(_('Profile: "%s"') % profile_name + '\n' + _('Snapshots folder is not valid !')) + self.notifyError( + '{}\n{}'.format( + _('Profile: "{name}"').format(name=profile_name), + _('Backup directory is not valid.') + ) + ) return False - #check include + # check include include_list = self.include(profile_id) if not include_list: - self.notifyError(_('Profile: "%s"') % profile_name + '\n' + _('You must select at least one folder to backup !')) + self.notifyError( + '{}\n{}'.format( + _('Profile: "{name}"').format(name=profile_name), + _('At least one directory must be selected ' + 'for backup.') + ) + ) + return False snapshots_path2 = snapshots_path + '/' @@ -311,141 +325,119 @@ def checkConfig(self): path = item[0] if path == snapshots_path: - self.notifyError(_('Profile: "%s"') % profile_name + '\n' + _('You can\'t include backup folder !')) + self.notifyError( + '{}\n{}\n{}'.format( + _('Profile: "{name}"').format(name=profile_name), + _('Directory: {path}').format(path=path), + _('This directory cannot be included in the ' + 'backup as it is part of the backup ' + 'destination itself.') + ) + ) + return False if len(path) >= len(snapshots_path2): if path[: len(snapshots_path2)] == snapshots_path2: - self.notifyError(_('Profile: "%s"') % self.currentProfile() + '\n' + _('You can\'t include a backup sub-folder !')) + self.notifyError( + '{}\n{}\n{}'.format( + _('Profile: "{name}"').format( + name=profile_name), + _('Directory: {path}').format(path=path), + _('This directory cannot be included in the ' + 'backup as it is part of the backup ' + 'destination itself.') + ) + ) + return False - return True - def user(self): - """ - portable way to get username - cc by-sa 3.0 http://stackoverflow.com/a/19865396/1139841 - author: techtonik http://stackoverflow.com/users/239247/techtonik - """ - if pwd: - return pwd.getpwuid(os.geteuid()).pw_name - else: - return getpass.getuser() + # check warn free space + if (self.warnFreeSpaceEnabled(profile_id) + and self.minFreeSpaceEnabled(profile_id)): + + warn = self.warnFreeSpace(profile_id) + _enabled, min_free = self.minFreeSpaceAsStorageSize(profile_id) + + if warn < min_free: + self.notifyError( + '{}\n{}\n{}'.format( + _('Profile: "{name}"').format(name=profile_name), + _('The value for "Remove oldest backup if the ' + 'free space is less than" ({val_one}) must be ' + 'less than or equal the threshold for "Warn if ' + 'free disk space falls below" ({val_two}).' + ).format(val_one=min_free, val_two=warn), + _('Please adjust the settings so that the backup ' + 'removal limit is not higher than the ' + 'warning limit.') + )) + return False - def pid(self): - return str(os.getpid()) + return True def host(self): return socket.gethostname() - def snapshotsPath(self, profile_id = None, mode = None, tmp_mount = False): + def get_snapshots_mountpoint(self, profile_id=None, mode=None, tmp_mount=False): + """Return the profiles snapshot path in form of a mount point.""" + if profile_id is None: + profile_id = self.currentProfile() + if mode is None: mode = self.snapshotsMode(profile_id) - if self.SNAPSHOT_MODES[mode][0] == None: - #no mount needed - #?Where to save snapshots in mode 'local'. This path must contain a - #?folderstructure like 'backintime///';absolute path - return self.profileStrValue('snapshots.path', '', profile_id) - else: - #mode need to be mounted; return mountpoint - symlink = self.snapshotsSymlink(profile_id = profile_id, tmp_mount = tmp_mount) - return os.path.join(self._LOCAL_MOUNT_ROOT, symlink) - def snapshotsFullPath(self, profile_id = None): - """ - Returns the full path for the snapshots: .../backintime/machine/user/profile_id/ - """ - host, user, profile = self.hostUserProfile(profile_id) - return os.path.join(self.snapshotsPath(profile_id), 'backintime', host, user, profile) + if mode == 'local': + return self.get_snapshots_path(profile_id) - def setSnapshotsPath(self, value, profile_id = None, mode = None): - """ - Sets the snapshot path to value, initializes, and checks it - """ - if not value: - return False + # else: ssh/local_encfs/ssh_encfs/local_gocryptfs - if profile_id == None: - profile_id = self.currentProfile() + symlink = f'{profile_id}_{os.getpid()}' + if tmp_mount: + symlink = f'tmp_{symlink}' - if mode is None: - mode = self.snapshotsMode(profile_id) + return os.path.join(self._LOCAL_MOUNT_ROOT, symlink) - if not os.path.isdir(value): - self.notifyError(_('%s is not a folder !') % value) - return False + def snapshotsPath(self, profile_id=None, mode=None, tmp_mount=False): + """Return the snapshot path (backup destination) as a mount point. - #Initialize the snapshots folder - logger.debug("Check snapshot folder: %s" %value, self) + That method is a surrogate for `self.get_snapshots_mountpoint()`. + """ + return self.get_snapshots_mountpoint( + profile_id=profile_id, + mode=mode, + tmp_mount=tmp_mount) + def snapshotsFullPath(self, profile_id = None): + """ + Returns the full path for the snapshots: .../backintime/machine/user/profile_id/ + """ host, user, profile = self.hostUserProfile(profile_id) + return os.path.join(self.snapshotsPath(profile_id), 'backintime', host, user, profile) - if not all((host, user, profile)): - self.notifyError(_('Host/User/Profile-ID must not be empty!')) - return False + def get_snapshots_path(self, profile_id): + """Return the value of the snapshot path (backup destination) field.""" + return self.profileStrValue('snapshots.path', '', profile_id) - full_path = os.path.join(value, 'backintime', host, user, profile) - if not os.path.isdir(full_path): - logger.debug("Create folder: %s" %full_path, self) - tools.makeDirs(full_path) - if not os.path.isdir(full_path): - self.notifyError(_('Can\'t write to: %s\nAre you sure you have write access ?' % value)) - return False - - for p in (os.path.join(value, 'backintime'), - os.path.join(value, 'backintime', host)): - try: - os.chmod(p, 0o777) - except PermissionError as e: - msg = "Failed to change permissions world writeable for '{}': {}" - logger.warning(msg.format(p, str(e)), self) - - #Test filesystem - fs = tools.filesystem(full_path) - if fs == 'vfat': - self.notifyError(_("Destination filesystem for '%(path)s' is formatted with FAT which doesn't support hard-links. " - "Please use a native Linux filesystem.") % - {'path': value}) - return False - elif fs == 'cifs' and not self.copyLinks(): - self.notifyError(_("Destination filsystem for '%(path)s' is a SMB mounted share. Please make sure " - "the remote SMB server supports symlinks or activate '%(copyLinks)s' in '%(expertOptions)s'.") % - {'path': value, - 'copyLinks': _('Copy links (dereference symbolic links)'), - 'expertOptions': _('Expert Options')}) - elif fs == 'fuse.sshfs' and mode not in ('ssh', 'ssh_encfs'): - self.notifyError(_("Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't support hard-links. " - "Please use mode 'SSH' instead.") % - {'path': value}) - return False + def set_snapshots_path(self, value, profile_id=None): + """Sets the snapshot path to value.""" + if profile_id is None: + profile_id = self.currentProfile() - #Test write access for the folder - check_path = os.path.join(full_path, 'check') - tools.makeDirs(check_path) - if not os.path.isdir(check_path): - self.notifyError(_('Can\'t write to: %s\nAre you sure you have write access ?' % full_path)) - return False + self.setProfileStrValue('snapshots.path', value, profile_id) - os.rmdir(check_path) - if self.SNAPSHOT_MODES[mode][0] is None: - self.setProfileStrValue('snapshots.path', value, profile_id) - return True + # def is_mode_encrypted(self, profile_id=None): + # mode = self.snapshotsMode(profile_id) + # return mode in ('local_encfs', 'ssh_encfs') - def snapshotsMode(self, profile_id = None): - #?Use mode (or backend) for this snapshot. Look at 'man backintime' - #?section 'Modes'.;local|local_encfs|ssh|ssh_encfs + def snapshotsMode(self, profile_id=None): + #? Use mode (or backend) for this snapshot. Look at 'man backintime' + #? section 'Modes'.;local|local_encfs|ssh|ssh_encfs|local_gocryptfs return self.profileStrValue('snapshots.mode', 'local', profile_id) def setSnapshotsMode(self, value, profile_id = None): self.setProfileStrValue('snapshots.mode', value, profile_id) - def snapshotsSymlink(self, profile_id = None, tmp_mount = False): - if profile_id is None: - profile_id = self.current_profile_id - symlink = '%s_%s' % (profile_id, self.pid()) - if tmp_mount: - symlink = 'tmp_%s' % symlink - return symlink - def setCurrentHashId(self, hash_id): self.current_hash_id = hash_id @@ -457,6 +449,23 @@ def incrementHashCollision(self): value = self.hashCollision() + 1 self.setIntValue('global.hash_collision', value) + def systray(self) -> str: + #?Color of systray icon.;auto,dark,light + return self.strValue('global.systray', 'auto') + + def set_systray(self, value: str) -> None: + self.setStrValue('global.systray', value) + + def language(self) -> str: + #?Language code (ISO 639) used to translate the user interface. + #?If empty the operating systems current local is used. If 'en' the + #?translation is not active and the original English source strings + #?are used. It is the same if the value is unknown. + return self.strValue('global.language', '') + + def setLanguage(self, language: str): + self.setStrValue('global.language', language if language else '') + # SSH def sshSnapshotsPath(self, profile_id = None): #?Snapshot path on remote host. If the path is relative (no leading '/') @@ -505,7 +514,7 @@ def setSshCipher(self, value, profile_id = None): def sshUser(self, profile_id = None): #?Remote SSH user;;local users name - return self.profileStrValue('snapshots.ssh.user', self.user(), profile_id) + return self.profileStrValue('snapshots.ssh.user', getpass.getuser(), profile_id) def setSshUser(self, value, profile_id = None): self.setProfileStrValue('snapshots.ssh.user', value, profile_id) @@ -520,34 +529,55 @@ def sshHostUserPortPathCipher(self, profile_id = None): path = './' return (host, port, user, path, cipher) - def sshPrivateKeyFile(self, profile_id = None): - ssh = self.sshPrivateKeyFolder() - default = '' - for f in ['id_dsa', 'id_rsa', 'identity']: - private_key = os.path.join(ssh, f) - if os.path.isfile(private_key): - default = private_key - break - #?Private key file used for password-less authentication on remote host. - #?;absolute path to private key file;~/.ssh/id_dsa - f = self.profileStrValue('snapshots.ssh.private_key_file', default, profile_id) - if f: - return f - return default - - def sshPrivateKeyFolder(self): - return os.path.join(os.path.expanduser('~'), '.ssh') - - def setSshPrivateKeyFile(self, value, profile_id = None): + def sshPrivateKeyFile(self, profile_id=None) -> None | bool | str: + """The field can have three states: + 1. Field does not exists: Fresh profile. Provide a default value. + 2. Field exist but is empty: Using keys is disabled. + 3. Field has a path: + """ + val = self.profileStrValue('snapshots.ssh.private_key_file', None, profile_id) + + # Using keys is disabled + if val == '': + return False + + return val + + def sshPrivateKeyFile_enabled(self, profile_id=None): + return self.sshPrivateKeyFile(profile_id) is not False + + def setSshPrivateKeyFile(self, value, profile_id=None): self.setProfileStrValue('snapshots.ssh.private_key_file', value, profile_id) + def sshProxyHost(self, profile_id=None): + #?Proxy host used to connect to remote host.;;IP or domain address + return self.profileStrValue('snapshots.ssh.proxy_host', '', profile_id) + + def setSshProxyHost(self, value, profile_id=None): + self.setProfileStrValue('snapshots.ssh.proxy_host', value, profile_id) + + def sshProxyPort(self, profile_id=None): + #?Proxy host port used to connect to remote host.;0-65535 + return self.profileIntValue('snapshots.ssh.proxy_host_port', '22', profile_id) + + def setSshProxyPort(self, value, profile_id = None): + self.setProfileIntValue('snapshots.ssh.proxy_host_port', value, profile_id) + + def sshProxyUser(self, profile_id=None): + #?Remote SSH user;;the local users name + return self.profileStrValue('snapshots.ssh.proxy_user', getpass.getuser(), profile_id) + + def setSshProxyUser(self, value, profile_id=None): + self.setProfileStrValue('snapshots.ssh.proxy_user', value, profile_id) + def sshMaxArgLength(self, profile_id = None): - #?Maximum argument length of commands run on remote host. This can be tested - #?with 'python3 /usr/share/backintime/common/sshMaxArg.py USER@HOST'.\n + #?Maximum command length of commands run on remote host. This can be tested + #?for all ssh profiles in the configuration + #?with 'python3 /usr/share/backintime/common/ssh_max_arg.py LENGTH'.\n #?0 = unlimited;0, >700 value = self.profileIntValue('snapshots.ssh.max_arg_length', 0, profile_id) if value and value < 700: - raise ValueError('SSH max arg length %s is to low to run commands' % value) + raise ValueError('SSH max arg length %s is too low to run commands' % value) return value def setSshMaxArgLength(self, value, profile_id = None): @@ -577,24 +607,30 @@ def sshDefaultArgs(self, profile_id = None): """ # keep connection alive args = ['-o', 'ServerAliveInterval=240'] + # disable ssh banner args += ['-o', 'LogLevel=Error'] + # specifying key file here allows to override for potentially # conflicting .ssh/config key entry - args += ['-o', 'IdentityFile={}'.format(self.sshPrivateKeyFile(profile_id))] + if self.sshPrivateKeyFile_enabled(profile_id): + key_file = self.sshPrivateKeyFile(profile_id) + if key_file: + args += ['-o', f'IdentityFile={key_file}'] + return args def sshCommand(self, - cmd = None, - custom_args = None, - port = True, - cipher = True, - user_host = True, - ionice = True, - nice = True, - quote = False, - prefix = True, - profile_id = None): + cmd=None, + custom_args=None, + port=True, + cipher=True, + user_host=True, + ionice=True, + nice=True, + quote=False, + prefix=True, + profile_id=None): """ Return SSH command with all arguments. @@ -613,20 +649,47 @@ def sshCommand(self, Returns: list: ssh command with chosen arguments """ + # Refactor: Use of assert is discouraged in productive code. + # Raise Exceptions instead. assert cmd is None or isinstance(cmd, list), "cmd '{}' is not list instance".format(cmd) assert custom_args is None or isinstance(custom_args, list), "custom_args '{}' is not list instance".format(custom_args) - ssh = ['ssh'] + + ssh = ['ssh'] ssh += self.sshDefaultArgs(profile_id) + + # Proxy (aka Jump host) + if self.sshProxyHost(profile_id): + ssh += ['-J', '{}@{}:{}'.format( + self.sshProxyUser(profile_id), + self.sshProxyHost(profile_id), + self.sshProxyPort(profile_id) + )] + # remote port if port: ssh += ['-p', str(self.sshPort(profile_id))] + # cipher used to transfer data c = self.sshCipher(profile_id) - if cipher and c != 'default': - ssh += ['-o', 'Ciphers={}'.format(c)] + if c != 'default': + # Using cipher is deprecated (#2143) and will be removed (#2176) + # in foreseen future. + logger.critical( + 'Using a configured cipher in Back In Time is deprecated. ' + f'Configured cipher: "{c}". Behavior will be removed in a ' + 'future release. Configure the cipher using the SSH client ' + 'config file instead. First remove key "profile.snapshots' + '.ssh.cipher=" from Back In Time\'s config file ' + '("~/.config/backintime/config").' + ) + + if cipher: + ssh += ['-o', f'Ciphers={c}'] + # custom arguments if custom_args: ssh += custom_args + # user@host if user_host: ssh.append('{}@{}'.format(self.sshUser(profile_id), @@ -634,25 +697,32 @@ def sshCommand(self, # quote the command running on remote host if quote and cmd: ssh.append("'") + # run 'ionice' on remote host if ionice and self.ioniceOnRemote(profile_id) and cmd: ssh += ['ionice', '-c2', '-n7'] + # run 'nice' on remote host if nice and self.niceOnRemote(profile_id) and cmd: ssh += ['nice', '-n19'] + # run prefix on remote host if prefix and cmd and self.sshPrefixEnabled(profile_id): - ssh += self.sshPrefixCmd(profile_id, cmd_type = list) + ssh += self.sshPrefixCmd(profile_id, cmd_type=type(cmd)) + # add the command if cmd: ssh += cmd + # close quote if quote and cmd: ssh.append("'") + logger.debug(f'SSH command: {ssh}', self) + return ssh - #ENCFS + # EncFS def localEncfsPath(self, profile_id = None): #?Where to save snapshots in mode 'local_encfs'.;absolute path return self.profileStrValue('snapshots.local_encfs.path', '', profile_id) @@ -660,6 +730,14 @@ def localEncfsPath(self, profile_id = None): def setLocalEncfsPath(self, value, profile_id = None): self.setProfileStrValue('snapshots.local_encfs.path', value, profile_id) + # gocryptfs + def localGocryptfsPath(self, profile_id = None): + #?Where to save snapshots in mode 'local_gocryptfs'.;absolute path + return self.profileStrValue('snapshots.local_gocryptfs.path', '', profile_id) + + def setLocalGocryptfsPath(self, value, profile_id = None): + self.setProfileStrValue('snapshots.local_gocryptfs.path', value, profile_id) + def passwordSave(self, profile_id = None, mode = None): if mode is None: mode = self.snapshotsMode(profile_id) @@ -675,34 +753,46 @@ def setPasswordSave(self, value, profile_id = None, mode = None): def passwordUseCache(self, profile_id = None, mode = None): if mode is None: mode = self.snapshotsMode(profile_id) - default = not tools.checkHomeEncrypt() #?Cache password in RAM so it can be read by cronjobs. #?Security issue: root might be able to read that password, too. - #? must be the same as \fIprofile.snapshots.mode\fR;;true if home is not encrypted - return self.profileBoolValue('snapshots.%s.password.use_cache' % mode, default, profile_id) + #? must be the same as \fIprofile.snapshots.mode\fR;;true + return self.profileBoolValue('snapshots.%s.password.use_cache' % mode, True, profile_id) def setPasswordUseCache(self, value, profile_id = None, mode = None): if mode is None: mode = self.snapshotsMode(profile_id) self.setProfileBoolValue('snapshots.%s.password.use_cache' % mode, value, profile_id) - def password(self, parent = None, profile_id = None, mode = None, pw_id = 1, only_from_keyring = False): + def password(self, + parent=None, + profile_id=None, + mode=None, + pw_id=1, + only_from_keyring=False): + if self.pw is None: self.pw = password.Password(self) + if profile_id is None: profile_id = self.currentProfile() + if mode is None: mode = self.snapshotsMode(profile_id) - return self.pw.password(parent, profile_id, mode, pw_id, only_from_keyring) - def setPassword(self, password, profile_id = None, mode = None, pw_id = 1): + return self.pw.password( + parent, profile_id, mode, pw_id, only_from_keyring) + + def setPassword(self, password_value, profile_id=None, mode=None, pw_id=1): if self.pw is None: self.pw = password.Password(self) + if profile_id is None: profile_id = self.currentProfile() + if mode is None: mode = self.snapshotsMode(profile_id) - self.pw.setPassword(password, profile_id, mode, pw_id) + + self.pw.setPassword(password_value, profile_id, mode, pw_id) def modeNeedPassword(self, mode, pw_id = 1): need_pw = self.SNAPSHOT_MODES[mode][pw_id + 1] @@ -722,9 +812,9 @@ def keyringUserName(self, profile_id = None): profile_id = self.currentProfile() return 'profile_id_%s' % profile_id - def hostUserProfileDefault(self, profile_id = None): + def hostUserProfileDefault(self, profile_id=None): host = socket.gethostname() - user = self.user() + user = getpass.getuser() profile = profile_id if profile is None: profile = self.currentProfile() @@ -749,48 +839,21 @@ def setHostUserProfile(self, host, user, profile, profile_id = None): self.setProfileStrValue('snapshots.path.user', user, profile_id) self.setProfileStrValue('snapshots.path.profile', profile, profile_id) - def includeV4(self, profile_id = None): - #?!ignore this in manpage - value = self.profileStrValue('snapshots.include_folders', '', profile_id) - if not value: - return [] - - paths = [] - - for item in value.split(':'): - fields = item.split('|') - - path = os.path.expanduser(fields[0]) - path = os.path.abspath(path) - paths.append(path) - - return paths - - def include(self, profile_id = None): + def include(self, profile_id=None): #?Include this file or folder. must be a counter starting with 1;absolute path:: #?Specify if \fIprofile.snapshots.include..value\fR is a folder (0) or a file (1).;0|1;0 - return self.profileListValue('snapshots.include', ('str:value', 'int:type'), [], profile_id) + return self.profileListValue(key='snapshots.include', type_key=('str:value', 'int:type'), default=[], profile_id=profile_id) def setInclude(self, values, profile_id = None): self.setProfileListValue('snapshots.include', ('str:value', 'int:type'), values, profile_id) - def excludeV4(self, profile_id = None): - """ - Gets the exclude patterns: conf version 4 - """ - #?!ignore this in manpage - value = self.profileStrValue('snapshots.exclude_patterns', '.gvfs:.cache*:[Cc]ache*:.thumbnails*:[Tt]rash*:*.backup*:*~', profile_id) - if not value: - return [] - return value.split(':') - def exclude(self, profile_id = None): """ Gets the exclude patterns """ #?Exclude this file or folder. must be a counter #?starting with 1;file, folder or pattern (relative or absolute) - return self.profileListValue('snapshots.exclude', 'str:value', self.DEFAULT_EXCLUDE, profile_id) + return self.profileListValue('snapshots.exclude', 'str:value', [], profile_id) def setExclude(self, values, profile_id = None): self.setProfileListValue('snapshots.exclude', 'str:value', values, profile_id) @@ -825,16 +888,42 @@ def scheduleMode(self, profile_id = None): #?25 = daily anacron\n27 = when drive get connected\n30 = every week\n #?40 = every month\n80 = every year #?;0|1|2|4|7|10|12|14|16|18|19|20|25|27|30|40|80;0 - return self.profileIntValue('schedule.mode', self.NONE, profile_id) + value = self.profileIntValue('schedule.mode', Config.NONE.value, profile_id) + return bitbase.ScheduleMode(value) def setScheduleMode(self, value, profile_id = None): + if isinstance(value, bitbase.ScheduleMode): + value = value.value self.setProfileIntValue('schedule.mode', value, profile_id) + def schedule_offset(self, profile_id = None): + return self.profileIntValue('schedule.offset', Config.DEFAULT_OFFSET, profile_id) + + def set_schedule_offset(self, value, profile_id = None): + self.setProfileIntValue('schedule.offset', value, profile_id) + + def scheduleDebug(self, profile_id = None): + #?Enable debug output to system log for schedule mode. + return self.profileBoolValue('schedule.debug', False, profile_id) + + def setScheduleDebug(self, value, profile_id = None): + self.setProfileBoolValue('schedule.debug', value, profile_id) + def scheduleTime(self, profile_id = None): - #?What time the cronjob should run? Only valid for - #?\fIprofile.schedule.mode\fR >= 20;0-24 + #?Position-coded number with the format "hhmm" to specify the hour + #?and minute the cronjob should start (eg. 2015 means a quarter + #?past 8pm). Leading zeros can be omitted (eg. 30 = 0030). + #?Only valid for + #?\fIprofile.schedule.mode\fR = 20 (daily), 30 (weekly), + #?40 (monthly) and 80 (yearly);0-2400 return self.profileIntValue('schedule.time', 0, profile_id) + def scheduleHourMinute(self, profile_id: str = None + ) -> tuple[int, int]: + the_time = self.scheduleTime(profile_id) + + return (the_time // 100, the_time % 100) + def setScheduleTime(self, value, profile_id = None): self.setProfileIntValue('schedule.time', value, profile_id) @@ -876,9 +965,12 @@ def scheduleRepeatedUnit(self, profile_id = None): #?10 = hours\n20 = days\n30 = weeks\n40 = months\n #?Only valid for \fIprofile.schedule.mode\fR = 25|27; #?10|20|30|40;20 - return self.profileIntValue('schedule.repeatedly.unit', self.DAY, profile_id) + value = self.profileIntValue('schedule.repeatedly.unit', bitbase.TimeUnit.DAY.value, profile_id) + return bitbase.TimeUnit(value) def setScheduleRepeatedUnit(self, value, profile_id = None): + if isinstance(value, bitbase.TimeUnit): + value = value.value self.setProfileIntValue('schedule.repeatedly.unit', value, profile_id) def removeOldSnapshots(self, profile_id = None): @@ -887,7 +979,7 @@ def removeOldSnapshots(self, profile_id = None): #?Snapshots older than this times units will be removed self.profileIntValue('snapshots.remove_old_snapshots.value', 10, profile_id), #?20 = days\n30 = weeks\n80 = years;20|30|80;80 - self.profileIntValue('snapshots.remove_old_snapshots.unit', self.YEAR, profile_id)) + bitbase.TimeUnit(self.profileIntValue('snapshots.remove_old_snapshots.unit', bitbase.TimeUnit.YEAR, profile_id))) def keepOnlyOneSnapshot(self, profile_id = None): #?NOT YET IMPLEMENTED. Remove all snapshots but one. @@ -899,63 +991,69 @@ def setKeepOnlyOneSnapshot(self, value, profile_id = None): def removeOldSnapshotsEnabled(self, profile_id = None): return self.profileBoolValue('snapshots.remove_old_snapshots.enabled', True, profile_id) - def removeOldSnapshotsDate(self, profile_id = None): + def removeOldSnapshotsDate(self, profile_id=None): enabled, value, unit = self.removeOldSnapshots(profile_id) if not enabled: return datetime.date(1, 1, 1) - if unit == self.DAY: - date = datetime.date.today() - date = date - datetime.timedelta(days = value) - return date - - if unit == self.WEEK: - date = datetime.date.today() - date = date - datetime.timedelta(days = date.weekday() + 7 * value) - return date - - if unit == self.YEAR: - date = datetime.date.today() - return date.replace(day = 1, year = date.year - value) - - return datetime.date(1, 1, 1) + return _remove_old_snapshots_date(value, unit) def setRemoveOldSnapshots(self, enabled, value, unit, profile_id = None): self.setProfileBoolValue('snapshots.remove_old_snapshots.enabled', enabled, profile_id) self.setProfileIntValue('snapshots.remove_old_snapshots.value', value, profile_id) self.setProfileIntValue('snapshots.remove_old_snapshots.unit', unit, profile_id) + def warnFreeSpaceEnabled(self, profile_id=None): + value = self.profileIntValue('snapshots.warn_free_space.value', 0, profile_id) + return value > 0 + + def warnFreeSpace(self, profile_id=None) -> StorageSize: + value = self.profileIntValue('snapshots.warn_free_space.value', 0, profile_id) + unit = self.profileIntValue('snapshots.warn_free_space.unit', SizeUnit.MIB, profile_id) + return StorageSize(value, SizeUnit(unit)) + + def setWarnFreeSpaceDisabled(self, profile_id=None): + self.setWarnFreeSpace(value=StorageSize(0, SizeUnit.MIB), profile_id=profile_id) + + def setWarnFreeSpace(self, value: StorageSize, profile_id=None): + self.setProfileIntValue('snapshots.warn_free_space.value', value.value(), profile_id) + self.setProfileIntValue('snapshots.warn_free_space.unit', value.unit.value, profile_id) + def minFreeSpace(self, profile_id = None): #?Remove snapshots until \fIprofile.snapshots.min_free_space.value\fR #?free space is reached. - return (self.profileBoolValue('snapshots.min_free_space.enabled', True, profile_id), - #?Keep at least value + unit free space.;1-99999 - self.profileIntValue('snapshots.min_free_space.value', 1, profile_id), - #?10 = MB\n20 = GB;10|20;20 - self.profileIntValue('snapshots.min_free_space.unit', self.DISK_UNIT_GB, profile_id)) - - def minFreeSpaceEnabled(self, profile_id = None): - return self.profileBoolValue('snapshots.min_free_space.enabled', True, profile_id) - - def minFreeSpaceMib(self, profile_id = None): + return ( + self.profileBoolValue('snapshots.min_free_space.enabled', True, profile_id), + #?Keep at least value + unit free space.;1-99999 + self.profileIntValue('snapshots.min_free_space.value', 1, profile_id), + #?10 = MB\n20 = GB;10|20;20 + SizeUnit(self.profileIntValue('snapshots.min_free_space.unit', SizeUnit.GIB, profile_id)) + ) + + def minFreeSpaceAsStorageSize(self, profile_id = None): enabled, value, unit = self.minFreeSpace(profile_id) - if not enabled: - return 0 - if self.DISK_UNIT_MB == unit: - return value + return ( + enabled, + StorageSize(value, SizeUnit(unit)) + ) - value *= 1024 #Gb - if self.DISK_UNIT_GB == unit: - return value - - return 0 + def minFreeSpaceEnabled(self, profile_id = None): + return self.profileBoolValue('snapshots.min_free_space.enabled', False, profile_id) def setMinFreeSpace(self, enabled, value, unit, profile_id = None): self.setProfileBoolValue('snapshots.min_free_space.enabled', enabled, profile_id) self.setProfileIntValue('snapshots.min_free_space.value', value, profile_id) self.setProfileIntValue('snapshots.min_free_space.unit', unit, profile_id) + def setMinFreeSpaceWithStorageSize(self, enabled, value: StorageSize, profile_id = None): + self.setMinFreeSpace( + enabled=enabled, + value=value.value(), + unit=value.unit.value, + profile_id=profile_id + ) + def minFreeInodes(self, profile_id = None): #?Keep at least value % free inodes.;1-15 return self.profileIntValue('snapshots.min_free_inodes.value', 2, profile_id) @@ -963,7 +1061,7 @@ def minFreeInodes(self, profile_id = None): def minFreeInodesEnabled(self, profile_id = None): #?Remove snapshots until \fIprofile.snapshots.min_free_inodes.value\fR #?free inodes in % is reached. - return self.profileBoolValue('snapshots.min_free_inodes.enabled', True, profile_id) + return self.profileBoolValue('snapshots.min_free_inodes.enabled', False, profile_id) def setMinFreeInodes(self, enabled, value, profile_id = None): self.setProfileBoolValue('snapshots.min_free_inodes.enabled', enabled, profile_id) @@ -1140,13 +1238,21 @@ def setCopyUnsafeLinks(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.copy_unsafe_links', value, profile_id) def copyLinks(self, profile_id = None): - #?When symlinks are encountered, the item that they point to + #?When symlinks are encountered, the item that they point to #?(the reference) is copied, rather than the symlink. return self.profileBoolValue('snapshots.copy_links', False, profile_id) def setCopyLinks(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.copy_links', value, profile_id) + def oneFileSystem(self, profile_id = None): + #?Use rsync's "--one-file-system" to avoid crossing filesystem + #?boundaries when recursing. + return self.profileBoolValue('snapshots.one_file_system', False, profile_id) + + def setOneFileSystem(self, value, profile_id = None): + return self.setProfileBoolValue('snapshots.one_file_system', value, profile_id) + def rsyncOptionsEnabled(self, profile_id = None): #?Past additional options to rsync return self.profileBoolValue('snapshots.rsync_options.enabled', False, profile_id) @@ -1174,17 +1280,25 @@ def setSshPrefix(self, enabled, value, profile_id = None): self.setProfileBoolValue('snapshots.ssh.prefix.enabled', enabled, profile_id) self.setProfileStrValue('snapshots.ssh.prefix.value', value, profile_id) - def sshPrefixCmd(self, profile_id = None, cmd_type = str): + def sshPrefixCmd(self, profile_id=None, cmd_type=str): + """Return the config value of sshPrefix if enabled. + + Dev note by buhtz (2024-04): Good opportunity to refactor. To much + implicit behavior in it. + """ if cmd_type == list: if self.sshPrefixEnabled(profile_id): return shlex.split(self.sshPrefix(profile_id)) - else: - return [] + + return [] + if cmd_type == str: if self.sshPrefixEnabled(profile_id): return self.sshPrefix(profile_id).strip() + ' ' - else: - return '' + + return '' + + raise TypeError(f'Unable to handle type {cmd_type}.') def continueOnErrors(self, profile_id = None): #?Continue on errors. This will keep incomplete snapshots rather than @@ -1215,13 +1329,6 @@ def takeSnapshotRegardlessOfChanges(self, profile_id = None): def setTakeSnapshotRegardlessOfChanges(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.take_snapshot_regardless_of_changes', value, profile_id) - def userCallbackNoLogging(self, profile_id = None): - #?Do not catch std{out|err} from user-callback script. - #?The script will only write to current TTY. - #?Default is to catch std{out|err} and write it to - #?syslog and TTY again. - return self.profileBoolValue('user_callback.no_logging', False, profile_id) - def globalFlock(self): #?Prevent multiple snapshots (from different profiles or users) to be run at the same time return self.boolValue('global.use_flock', False) @@ -1229,33 +1336,34 @@ def globalFlock(self): def setGlobalFlock(self, value): self.setBoolValue('global.use_flock', value) - def appPath(self): - return self._APP_PATH - - def docPath(self): - return self._DOC_PATH - def appInstanceFile(self): return os.path.join(self._LOCAL_DATA_FOLDER, 'app.lock') - def fileId(self, profile_id = None): + def fileId(self, profile_id=None): if profile_id is None: profile_id = self.currentProfile() + if profile_id == '1': return '' + return profile_id def takeSnapshotLogFile(self, profile_id = None): - return os.path.join(self._LOCAL_DATA_FOLDER, "takesnapshot_%s.log" % self.fileId(profile_id)) + return os.path.join(self._LOCAL_DATA_FOLDER, + "takesnapshot_%s.log" % self.fileId(profile_id)) def takeSnapshotMessageFile(self, profile_id = None): - return os.path.join(self._LOCAL_DATA_FOLDER, "worker%s.message" % self.fileId(profile_id)) + return os.path.join(self._LOCAL_DATA_FOLDER, + "worker%s.message" % self.fileId(profile_id)) def takeSnapshotProgressFile(self, profile_id = None): - return os.path.join(self._LOCAL_DATA_FOLDER, "worker%s.progress" % self.fileId(profile_id)) + return os.path.join(self._LOCAL_DATA_FOLDER, + "worker%s.progress" % self.fileId(profile_id)) - def takeSnapshotInstanceFile(self, profile_id = None): - return os.path.join(self._LOCAL_DATA_FOLDER, "worker%s.lock" % self.fileId(profile_id)) + def takeSnapshotInstanceFile(self, profile_id=None): + return os.path.join( + self._LOCAL_DATA_FOLDER, + "worker%s.lock" % self.fileId(profile_id)) def takeSnapshotUserCallback(self): return os.path.join(self._LOCAL_CONFIG_FOLDER, "user-callback") @@ -1275,338 +1383,341 @@ def passwordCacheInfo(self): def cronEnvFile(self): return os.path.join(self._LOCAL_DATA_FOLDER, "cron_env") - def anacrontab(self, suffix = ''): - """ - Deprecated since 1.1. Just keep this to delete old anacrontab files - """ - return os.path.join(self._LOCAL_CONFIG_FOLDER, 'anacrontab' + suffix) - - def anacrontabFiles(self): - """ - list existing old anacrontab files - """ - dirname, basename = os.path.split(self.anacrontab()) - for f in os.listdir(dirname): - if f.startswith(basename): - yield os.path.join(dirname, f) - def anacronSpool(self): + # ~/.local/share/backintime/anacron return os.path.join(self._LOCAL_DATA_FOLDER, 'anacron') - def anacronSpoolFile(self, profile_id = None): - return os.path.join(self.anacronSpool(), self.anacronJobIdentify(profile_id)) + def anacronSpoolFile(self, profile_id=None): + """Return the timestamp file related to the current profile. + + Despite the methods name anacron is not involved. But the anacron + behavior is imitated by Back In Time. This timestamp files are an + element of this behavior. + """ + # ~/.local/share/backintime/anacron/1_Main_profile + return os.path.join(self.anacronSpool(), + self.anacronJobIdentify(profile_id)) - def anacronJobIdentify(self, profile_id = None): + def anacronJobIdentify(self, profile_id=None): if not profile_id: profile_id = self.currentProfile() + profile_name = self.profileName(profile_id) + + # "Main profile" -> "1_Main_profile" return profile_id + '_' + profile_name.replace(' ', '_') def udevRulesPath(self): - return os.path.join('/etc/udev/rules.d', '99-backintime-%s.rules' % self.user()) + return os.path.join('/etc/udev/rules.d', '99-backintime-%s.rules' % getpass.getuser()) def restoreLogFile(self, profile_id = None): return os.path.join(self._LOCAL_DATA_FOLDER, "restore_%s.log" % self.fileId(profile_id)) - def restoreInstanceFile(self, profile_id = None): - return os.path.join(self._LOCAL_DATA_FOLDER, "restore%s.lock" % self.fileId(profile_id)) + def restoreInstanceFile(self, profile_id=None): + return os.path.join( + self._LOCAL_DATA_FOLDER, + "restore%s.lock" % self.fileId(profile_id)) def lastSnapshotSymlink(self, profile_id = None): - return os.path.join(self.snapshotsFullPath(profile_id), 'last_snapshot') + return os.path.join(self.snapshotsFullPath(profile_id), bitbase.DIR_NAME_LAST_SNAPSHOT) def encfsconfigBackupFolder(self, profile_id = None): return os.path.join(self._LOCAL_DATA_FOLDER, 'encfsconfig_backup_%s' % self.fileId(profile_id)) - def license(self): - return tools.readFile(os.path.join(self.docPath(), 'LICENSE'), '') + def isConfigured(self, profile_id=None) -> bool: + """Checks if the program is configured. - def translations(self): - return tools.readFile(os.path.join(self.docPath(), 'TRANSLATIONS'), '') - - def authors(self): - return tools.readFile(os.path.join(self.docPath(), 'AUTHORS'), '') + It is assumed as configured if a snapshot path (backup destination) is + and include files/directories (backup source) are given. + """ + path = self.snapshotsPath(profile_id) + includes = self.include(profile_id) - def changelog(self): - for i in ('CHANGES', 'changelog'): - f = os.path.join(self.docPath(), i) - clog = tools.readFile(f, '') - if clog: - return clog - return '' + if bool(path and includes): + return True - def preparePath(self, path): - if len(path) > 1: - if path[-1] == os.sep: - path = path[: -1] - return path + logger.debug(f'Profile ({profile_id=}) is not configured because ' + f'backup path is "{bool(path)}" and/or includes ' + f'are "{bool(includes)}".', self) - def isConfigured(self, profile_id = None): - """ - Checks if the program is configured - """ - return bool(self.snapshotsPath(profile_id) and self.include(profile_id)) + return False - def canBackup(self, profile_id = None): - """ - Checks if snapshots_path exists + def canBackup(self, profile_id=None): + """Checks if snapshots_path exists. """ if not self.isConfigured(profile_id): return False - if not os.path.isdir(self.snapshotsFullPath(profile_id)): - logger.error("%s does not exist" - %self.snapshotsFullPath(profile_id), - self) + path = self.snapshotsFullPath(profile_id) + + if not os.path.exists(path): + logger.warning(f'Snapshot path does not exists: {path}', self) + return False + + if not os.path.isdir(path): + logger.warning(f'Snapshot path is not a directory: {path}', self) return False return True def backupScheduled(self, profile_id = None): - """ - check if profile is supposed to be run this time - """ - if self.scheduleMode(profile_id) not in (self.REPEATEDLY, self.UDEV): - return True + """Check if the profile is supposed to be run this time. - #if crontab wasn't updated since upgrading BIT to version without anacron - #we are most likely started by anacron and should run this task without asking. - if list(self.anacrontabFiles()): + Returns: + (bool): The answer. + """ + if self.scheduleMode(profile_id) not in ( + bitbase.ScheduleMode.REPEATEDLY, + bitbase.ScheduleMode.UDEV): return True last_time = tools.readTimeStamp(self.anacronSpoolFile(profile_id)) if not last_time: return True - value = self.scheduleRepeatedPeriod(profile_id) - unit = self.scheduleRepeatedUnit(profile_id) + return tools.elapsed_at_least( + start=last_time, + end=datetime.datetime.now(), + value=self.scheduleRepeatedPeriod(profile_id), + unit=self.scheduleRepeatedUnit(profile_id) + ) - return self.olderThan(last_time, value, unit) + def setup_automation(self): + """Update schedule and event base automated backup job execution. - def olderThan(self, time, value, unit): + This affects crontab and udev rules. """ - return True if time is older than months, weeks, days or hours - """ - assert isinstance(time, datetime.datetime), 'time is not datetime.datetime type: %s' % time - - now = datetime.datetime.now() - - if unit <= self.HOUR: - return time < now - datetime.timedelta(hours = value) - elif unit <= self.DAY: - return time.date() <= now.date() - datetime.timedelta(days = value) - elif unit <= self.WEEK: - return time.date() < now.date() \ - - datetime.timedelta(days = now.date().weekday()) \ - - datetime.timedelta(weeks = value - 1) - elif unit <= self.MONTH: - firstDay = now.date() - datetime.timedelta(days = now.date().day + 1) - for i in range(value - 1): - if firstDay.month == 1: - firstDay = firstDay.replace(month = 12, year = firstDay.year - 1) - else: - firstDay = firstDay.replace(month = firstDay.month - 1) - return time.date() < firstDay - else: - return True - - SYSTEM_ENTRY_MESSAGE = "#Back In Time system entry, this will be edited by the gui:" + self._setup_schedule_based_automation() + self._setup_event_based_automation() - def setupCron(self): - for f in self.anacrontabFiles(): - logger.debug("Clearing anacrontab %s" - %f, self) - os.remove(f) + def _setup_event_based_automation(self): + """Update udev rules for event based automated profiles.""" self.setupUdev.clean() - oldCrontab = tools.readCrontab() + profile_ids = self.profile_ids_automated_via_udev_evnts() - strippedCrontab = self.removeOldCrontab(oldCrontab) - newCrontab = self.createNewCrontab(strippedCrontab) - if not isinstance(newCrontab, (list, tuple)): - return newCrontab + if not len(profile_ids): + return - #save Udev rules + for pid in profile_ids: + backup_mode = self.snapshotsMode(pid) + if backup_mode == 'local': + dest_path = self.snapshotsFullPath(pid) + elif backup_mode == 'local_gocryptfs': + dest_path = self.localGocryptfsPath(pid) + elif backup_mode == 'local_encfs': + dest_path = self.localEncfsPath(pid) + else: + logger.error( + f"Udev scheduling doesn't work with mode {backup_mode}", + self) + self.notifyError(_( + "Udev schedule doesn't work with mode {mode}") + .format(mode=backup_mode)) + return + + # Add rule + schedule.add_udev_rule( + pid=pid, + udev_setup=self.setupUdev, + dest_path=dest_path, + exec_command=self._cron_cmd(pid), + notify_callback=self.notifyError) + + # Save Udev rules try: if self.setupUdev.isReady and self.setupUdev.save(): logger.debug('Udev rules added successfully', self) - except PermissionDeniedByPolicy as e: - logger.error(str(e), self) - self.notifyError(str(e)) + + except PermissionDeniedByPolicy as err: + logger.error(str(err), self) + self.notifyError(str(err)) return False - if not newCrontab == oldCrontab: - if not tools.checkCommand('crontab'): - if self.scheduleMode() is self.NONE: - return True - else: - logger.error('crontab not found.', self) - self.notifyError(_('Can\'t find crontab.\nAre you sure cron is installed ?\n' - 'If not you should disable all automatic backups.')) - return False - if not tools.writeCrontab(newCrontab): - self.notifyError(_('Failed to write new crontab.')) - return False - else: - logger.debug("Crontab didn't change. Skip writing.") - return True + def _setup_schedule_based_automation(self): + """Update the current users crontab file based on profile settings. - def removeOldCrontab(self, crontab): - #We have to check if the self.SYSTEM_ENTRY_MESSAGE is in use, - #if not then the entries are most likely from Back In Time 0.9.26 - #or earlier. - if not self.SYSTEM_ENTRY_MESSAGE in crontab: - #Then the system entry message has not yet been used in this crontab - #therefore we assume all entries are system entries and clear them all. - #This is the old behaviour - logger.debug("Clearing all Back In Time entries", self) - return [x for x in crontab if not 'backintime' in x] - else: - #clear all line peers which have a SYSTEM_ENTRY_MESSAGE followed by - #one backintime command line - logger.debug("Clearing system Back In Time entries", self) - delLines = [] - for i, line in enumerate(crontab): - if self.SYSTEM_ENTRY_MESSAGE in line and \ - len(crontab) > i + 1 and \ - 'backintime' in crontab[i + 1]: - delLines.extend((i, i + 1)) - return [line for i, line in enumerate(crontab) if i not in delLines] - - def createNewCrontab(self, oldCrontab): - newCrontab = oldCrontab[:] - if not tools.checkCommand('backintime'): - logger.error("Command 'backintime' not found", self) - return newCrontab - for profile_id in self.profiles(): - cronLine = self.cronLine(profile_id) - if not isinstance(cronLine, str): - return cronLine - if cronLine: - newCrontab.append(self.SYSTEM_ENTRY_MESSAGE) - newCrontab.append(cronLine.replace('{cmd}', self.cronCmd(profile_id))) - - if newCrontab == oldCrontab: - # Leave one self.SYSTEM_ENTRY_MESSAGE in to prevent deleting of manual - # entries if there is no automatic entry. - newCrontab.append(self.SYSTEM_ENTRY_MESSAGE) - newCrontab.append("#Please don't delete these two lines, or all custom backintime " - "entries are going to be deleted next time you call the gui options!") - return newCrontab - - def cronLine(self, profile_id): - cron_line = '' - profile_name = self.profileName(profile_id) - backup_mode = self.scheduleMode(profile_id) - logger.debug("Profile: %s | Automatic backup: %s" - %(profile_name, self.SCHEDULE_MODES[backup_mode]), - self) + The crontab files is read, all entries related to Back In Time are + removed and after it added again for each profile based on the profile + settings. The difference between a backintime related entry created + by Back In Time itself or by the user manually is determined by a + comment before each entry. See :data:`schedule._MARKER` and + :func:`schedule.remove_bit_from_crontab()` for details. - if self.NONE == backup_mode: - return cron_line + Returns: + bool: ``True`` if successful or ``False`` on errors. + """ + + # Lines of current users crontab file + org_crontab_lines = schedule.read_crontab() + + # Remove all auto-generated BIT entries from crontab + crontab_lines = schedule.remove_bit_from_crontab(org_crontab_lines) + + # Add a new entry to existing crontab content based on the current + # snapshot profile and its schedule settings. + crontab_lines = schedule.append_bit_to_crontab( + crontab_lines, + self.profiles_cron_lines()) + + # Crontab modified? + if crontab_lines == org_crontab_lines: + return + + if schedule.write_crontab(crontab_lines) is False: + logger.error('Failed to write new crontab.') + self.notifyError(_('Failed to write new crontab.')) + return + + if not schedule.is_cron_running(): + logger.error( + 'Cron is not running despite the crontab command being ' + 'available. Scheduled backup jobs will not run.') + self.notifyError(_( + 'Cron is not running, even though the crontab command is ' + 'available. Scheduled backup jobs will not run. ' + 'Cron might be installed but not enabled. Try running the two ' + 'commands "systemctl enable cron" and ' + '"systemctl start cron", or consult the support channels of ' + 'the currently used GNU/Linux distribution for assistance.')) + + def profile_ids_automated_via_cron_schedule(self): + """Return list of profile ids configured to time based automation + using cron rules.""" + return list(filter( + lambda pid: bitbase.ScheduleMode(self.scheduleMode(pid)) + not in (bitbase.ScheduleMode.DISABLED, + bitbase.ScheduleMode.UDEV), + self.profiles() + )) + + def profile_ids_automated_via_udev_evnts(self): + """Return list of profile ids configured to event based automation + using udev.""" + + return list(filter( + lambda pid: bitbase.ScheduleMode(self.scheduleMode(pid)) + == bitbase.ScheduleMode.UDEV, + self.profiles() + )) + + def profiles_cron_lines(self): + """Return a list of crontab lines for each of the existing profiles. + + Return: + list: The list of crontab lines. + """ + profile_ids = self.profile_ids_automated_via_cron_schedule() + return [self._cron_line(pid) for pid in profile_ids] + + def _cron_line(self, profile_id) -> str: + """Return a cron line for the specified profile. + + Returns: + `None` in case of errors or profile is not configured for + scheduling. + """ + schedule_mode = self.scheduleMode(profile_id) + schedule_mode = bitbase.ScheduleMode(schedule_mode) - hour = self.scheduleTime(profile_id) // 100 - minute = self.scheduleTime(profile_id) % 100 + hour, minute = self.scheduleHourMinute(profile_id) day = self.scheduleDay(profile_id) weekday = self.scheduleWeekday(profile_id) + offset = str(self.schedule_offset(profile_id)) + + return schedule.create_cron_line( + schedule_mode=schedule_mode, + cron_command=self._cron_cmd(profile_id), + hour=hour, + minute=minute, + day=day, + weekday=weekday, + offset=offset, + custom_backup_time=self.customBackupTime(profile_id), + repeat_unit=bitbase.TimeUnit( + self.scheduleRepeatedUnit(profile_id)), + pid=profile_id, + notify_callback=self.notifyError) + + def _cron_cmd(self, profile_id): + """Generates the command used in the crontab file based on the settings + for the current profile. - if self.AT_EVERY_BOOT == backup_mode: - cron_line = '@reboot {cmd}' - elif self._5_MIN == backup_mode: - cron_line = '*/5 * * * * {cmd}' - elif self._10_MIN == backup_mode: - cron_line = '*/10 * * * * {cmd}' - elif self._30_MIN == backup_mode: - cron_line = '*/30 * * * * {cmd}' - elif self._1_HOUR == backup_mode: - cron_line = '0 * * * * {cmd}' - elif self._2_HOURS == backup_mode: - cron_line = '0 */2 * * * {cmd}' - elif self._4_HOURS == backup_mode: - cron_line = '0 */4 * * * {cmd}' - elif self._6_HOURS == backup_mode: - cron_line = '0 */6 * * * {cmd}' - elif self._12_HOURS == backup_mode: - cron_line = '0 */12 * * * {cmd}' - elif self.CUSTOM_HOUR == backup_mode: - cron_line = '0 ' + self.customBackupTime(profile_id) + ' * * * {cmd}' - elif self.DAY == backup_mode: - cron_line = '%s %s * * * {cmd}' % (minute, hour) - elif self.REPEATEDLY == backup_mode: - if self.scheduleRepeatedUnit(profile_id) <= self.DAY: - cron_line = '*/15 * * * * {cmd}' - else: - cron_line = '0 * * * * {cmd}' - elif self.UDEV == backup_mode: - if not self.setupUdev.isReady: - logger.error("Failed to install Udev rule for profile %s. " - "DBus Service 'net.launchpad.backintime.serviceHelper' not available" - %profile_id, self) - self.notifyError(_('Could not install Udev rule for profile %(profile_id)s. ' - 'DBus Service \'%(dbus_interface)s\' ' - 'wasn\'t available') - %{'profile_id': profile_id, - 'dbus_interface': 'net.launchpad.backintime.serviceHelper'}) - mode = self.snapshotsMode(profile_id) - if mode == 'local': - dest_path = self.snapshotsFullPath(profile_id) - elif mode == 'local_encfs': - dest_path = self.localEncfsPath(profile_id) - else: - logger.error('Schedule udev doesn\'t work with mode %s' %mode, self) - self.notifyError(_('Schedule udev doesn\'t work with mode %s') % mode) - return False - uuid = tools.uuidFromPath(dest_path) - if uuid is None: - #try using cached uuid - #?Devices uuid used to automatically set up udev rule if the drive is not connected. - uuid = self.profileStrValue('snapshots.path.uuid', '', profile_id) - if not uuid: - logger.error('Couldn\'t find UUID for "%s"' %dest_path, self) - self.notifyError(_('Couldn\'t find UUID for "%s"') % dest_path) - return False - else: - #cache uuid in config - self.setProfileStrValue('snapshots.path.uuid', uuid, profile_id) - try: - self.setupUdev.addRule(self.cronCmd(profile_id), uuid) - except (InvalidChar, InvalidCmd, LimitExceeded) as e: - logger.error(str(e), self) - self.notifyError(str(e)) - return False - elif self.WEEK == backup_mode: - cron_line = '%s %s * * %s {cmd}' %(minute, hour, weekday) - elif self.MONTH == backup_mode: - cron_line = '%s %s %s * * {cmd}' %(minute, hour, day) - elif self.YEAR == backup_mode: - cron_line = '%s %s 1 1 * {cmd}' %(minute, hour) - - return cron_line - - def cronCmd(self, profile_id): - if not tools.checkCommand('backintime'): - logger.error("Command 'backintime' not found", self) - return + Returns: + str: The crontab line. + """ + + # Get full path of the Back In Time binary cmd = tools.which('backintime') + ' ' + + # The "--profile-id" argument is used only for profiles different from + # first profile if profile_id != '1': - cmd += '--profile-id %s ' % profile_id + cmd += '--profile %s ' % profile_id + + # User defined path to config file if not self._LOCAL_CONFIG_PATH is self._DEFAULT_CONFIG_PATH: cmd += '--config %s ' % self._LOCAL_CONFIG_PATH - if logger.DEBUG: + + # Enable debug output + if self.scheduleDebug(profile_id): cmd += '--debug ' - cmd += 'backup-job' + + # command + cmd += 'backup --background' + + # Redirect stdout to nirvana if self.redirectStdoutInCron(profile_id): cmd += ' >/dev/null' + + # Redirect stderr ... if self.redirectStderrInCron(profile_id): + if self.redirectStdoutInCron(profile_id): + # ... to stdout cmd += ' 2>&1' else: + # ... to nirvana cmd += ' 2>/dev/null' + + # IO priority: low (-n7) in "best effort" class (-c2) if self.ioniceOnCron(profile_id) and tools.checkCommand('ionice'): cmd = tools.which('ionice') + ' -c2 -n7 ' + cmd + + # CPU priority: very low if self.niceOnCron(profile_id) and tools.checkCommand('nice'): cmd = tools.which('nice') + ' -n19 ' + cmd + return cmd -if __name__ == '__main__': - config = Config() - print("snapshots path = %s" % config.snapshotsFullPath()) + def addProfile(self, name: str) -> str | None: + pid = super().addProfile(name) + + if pid: + self._unsaved_profiles.append(pid) + + return pid + + +def _remove_old_snapshots_date(value, unit): + """Dev note (buhtz, 2025-01): The function exist to decople that code from + Config class and make it testable to investigate its behavior. + + See issue #1943 for further reading. + """ + if unit == Config.DAY: + date = datetime.date.today() + date = date - datetime.timedelta(days=value) + return date + + if unit == Config.WEEK: + date = datetime.date.today() + # Always beginning (Monday) of the week + date = date - datetime.timedelta(days=date.weekday() + 7 * value) + return date + + if unit == Config.YEAR: + date = datetime.date.today() + return date.replace(day=1, year=date.year - value) + + return datetime.date(1, 1, 1) diff --git a/common/configfile.py b/common/configfile.py index b760060af..39a4ff0ec 100644 --- a/common/configfile.py +++ b/common/configfile.py @@ -1,33 +1,23 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os import collections import re - -import gettext import logger -_=gettext.gettext -class ConfigFile(object): - """ - Store options in a plain text file in form of: key=value +class ConfigFile: + """Store options in a plain text file in form of: key=value """ + def __init__(self): self.dict = {} self.errorHandler = None @@ -65,6 +55,7 @@ def notifyError(self, message): """ if self.errorHandler is None: return + self.errorHandler(message) def askQuestion(self, message): @@ -76,6 +67,7 @@ def askQuestion(self, message): """ if self.questionHandler is None: return False + return self.questionHandler(message) def save(self, filename): @@ -90,20 +82,26 @@ def save(self, filename): """ def numsort(key): """ - Sort int in keys in nummeric order instead of alphabetical by adding + Sort int in keys in numeric order instead of alphabetical by adding leading zeros to int's """ return re.sub(r'\d+', lambda m: m.group(0).zfill(6), key) + try: - with open(filename, 'wt') as f: + with open(filename, mode='wt', encoding='utf-8') as f: keys = list(self.dict.keys()) - keys.sort(key = numsort) + keys.sort(key=numsort) + for key in keys: f.write("%s=%s\n" % (key, self.dict[key])) + except OSError as e: - logger.error('Failed to save config: %s' %str(e), self) - self.notifyError(_('Failed to save config: %s') %str(e)) + logger.error('Failed to save config: %s' % str(e), self) + self.notifyError( + '{}: {}'.format(_('Failed to save config'), str(e))) + return False + return True def load(self, filename, **kwargs): @@ -116,7 +114,7 @@ def load(self, filename, **kwargs): self.dict = {} self.append(filename, **kwargs) - def append(self, filename, maxsplit = 1): + def append(self, filename, maxsplit=1): """ Load options from file and append them to current options. @@ -128,17 +126,21 @@ def append(self, filename, maxsplit = 1): if not os.path.isfile(filename): return + try: - with open(filename, 'rt') as f: + with open(filename, mode='rt', encoding='utf-8') as f: lines = f.readlines() + except OSError as e: - logger.error('Failed to load config: %s' %str(e), self) - self.notifyError(_('Failed to load config: %s') %str(e)) + logger.error('Failed to load config: %s' % str(e), self) + self.notifyError( + '{}: {}'.format(_('Failed to load config'), str(e))) for line in lines: items = line.strip('\n').split('=', maxsplit) + if len(items) == 2: - self.dict[items[ 0 ] ] = items[ 1] + self.dict[items[0]] = items[1] def remapKey(self, old_key, new_key): """ @@ -149,9 +151,12 @@ def remapKey(self, old_key, new_key): new_key (str): new key name """ if old_key != new_key: + if old_key in self.dict: + if new_key not in self.dict: - self.dict[new_key ] = self.dict[ old_key] + self.dict[new_key] = self.dict[old_key] + del self.dict[old_key] def remapKeyRegex(self, pattern, replace): @@ -165,8 +170,10 @@ def remapKeyRegex(self, pattern, replace): to replace all matches of ``pattern``. """ c = re.compile(pattern) + for key in list(self.dict): newKey = c.sub(replace, key) + if key != newKey: self.remapKey(key, newKey) @@ -182,17 +189,16 @@ def hasKey(self, key): """ return key in self.dict - def strValue(self, key, default = ''): + def strValue(self, key, default=''): """ Return a 'str' instance of key's value. Args: - key (str): string used as key - default (str): return this if ``key`` is not set + key (str): Key identifying the value in the config file. + default (str): Default value if ``key`` is not present. Returns: - str: value of ``key`` or ``default`` - if ``key`` is not set. + str: Value of ``key`` or ``default``. """ if key in self.dict: return self.dict[key] @@ -209,7 +215,7 @@ def setStrValue(self, key, value): """ self.dict[key] = value - def intValue(self, key, default = 0): + def intValue(self, key, default=0): """ Return a 'int' instance of key's value. @@ -223,7 +229,8 @@ def intValue(self, key, default = 0): """ try: return int(self.dict[key]) - except: + + except Exception: return default def setIntValue(self, key, value): @@ -236,7 +243,7 @@ def setIntValue(self, key, value): """ self.setStrValue(key, str(value)) - def boolValue(self, key, default = False): + def boolValue(self, key, default=False): """ Return a 'bool' instance of key's value. @@ -250,10 +257,13 @@ def boolValue(self, key, default = False): """ try: val = self.dict[key] + if "1" == val or "TRUE" == val.upper(): return True + return False - except: + + except Exception: return default def setBoolValue(self, key, value): @@ -269,7 +279,7 @@ def setBoolValue(self, key, value): else: self.setStrValue(key, 'false') - def listValue(self, key, type_key = 'str:value', default = []): + def listValue(self, key, type_key='str:value', default=[]): """ Return a list of values @@ -295,36 +305,55 @@ def listValue(self, key, type_key = 'str:value', default = []): """ def typeKeySplit(tk): t, k = '', '' + if isinstance(tk, str): - t, k = tk.split(':', maxsplit = 1) + t, k = tk.split(':', maxsplit=1) + return (t, k) def value(key, tk): t, k = typeKeySplit(tk) + if t in ('str', 'int', 'bool'): - func = getattr(self, '%sValue' %t) - return func('%s.%s' %(key, k)) - raise TypeError('Invalid type_key: %s' %tk) + func = getattr(self, '%sValue' % t) + + return func('%s.%s' % (key, k)) + + raise TypeError('Invalid type_key: %s' % tk) + + size = self.intValue('%s.size' % key, -1) - size = self.intValue('%s.size' %key, -1) if size < 0: return default ret = [] + for i in range(1, size + 1): + if isinstance(type_key, str): - if not self.hasKey('%s.%s.%s' %(key, i, typeKeySplit(type_key)[1])): + + if not self.hasKey('%s.%s.%s' % + (key, i, typeKeySplit(type_key)[1])): continue - ret.append(value('%s.%s' %(key, i), type_key)) + + ret.append(value('%s.%s' % (key, i), type_key)) + elif isinstance(type_key, tuple): - if not self.hasKey('%s.%s.%s' %(key, i, typeKeySplit(type_key[0])[1])): + + if not self.hasKey('%s.%s.%s' % + (key, i, typeKeySplit(type_key[0])[1])): continue + items = [] + for tk in type_key: - items.append(value('%s.%s' %(key, i), tk)) + items.append(value('%s.%s' % (key, i), tk)) + ret.append(tuple(items)) + else: - raise TypeError('Invalid type_key: %s' %type_key) + raise TypeError('Invalid type_key: %s' % type_key) + return ret def setListValue(self, key, type_key, value): @@ -335,7 +364,8 @@ def setListValue(self, key, type_key, value): Args: key (str): used base-key - type_key (str): pattern of 'value-type:value-name'. See examples below. + type_key (str): pattern of 'value-type:value-name'. + See examples below. value (list): that should be stored ``type_key`` pattern examples:: @@ -348,38 +378,51 @@ def setListValue(self, key, type_key, value): """ def setValue(key, tk, v): t = '' + if isinstance(tk, str): - t, k = tk.split(':', maxsplit = 1) + t, k = tk.split(':', maxsplit=1) + if t in ('str', 'int', 'bool'): - func = getattr(self, 'set%sValue' %t.capitalize()) - return func('%s.%s' %(key, k), v) - raise TypeError('Invalid type_key: %s' %tk) + func = getattr(self, 'set%sValue' % t.capitalize()) + + return func('%s.%s' % (key, k), v) + + raise TypeError('Invalid type_key: %s' % tk) if not isinstance(value, (list, tuple)): - raise TypeError('value has wrong type: %s' %value) + raise TypeError('value has wrong type: %s' % value) + + old_size = self.intValue('%s.size' % key, -1) + self.setIntValue('%s.size' % key, len(value)) - old_size = self.intValue('%s.size' %key, -1) - self.setIntValue('%s.size' %key, len(value)) + for i, v in enumerate(value, start=1): - for i, v in enumerate(value, start = 1): if isinstance(type_key, str): - setValue('%s.%s' %(key, i), type_key, v) + setValue('%s.%s' % (key, i), type_key, v) + elif isinstance(type_key, tuple): + for iv, tk in enumerate(type_key): + if len(v) > iv: - setValue('%s.%s' %(key, i), tk, v[iv]) + setValue('%s.%s' % (key, i), tk, v[iv]) else: - self.removeKey('%s.%s.%s' %(key, i, tk.split(':')[1])) + self.removeKey('%s.%s.%s' % (key, i, tk.split(':')[1])) else: - raise TypeError('Invalid type_key: %s' %type_key) + raise TypeError('Invalid type_key: %s' % type_key) if len(value) < old_size: + for i in range(len(value) + 1, old_size + 1): + if isinstance(type_key, str): - self.removeKey('%s.%s.%s' %(key, i, type_key.split(':')[1])) + self.removeKey( + '%s.%s.%s' % (key, i, type_key.split(':')[1])) + elif isinstance(type_key, tuple): for tk in type_key: - self.removeKey('%s.%s.%s' %(key, i, tk.split(':')[1])) + self.removeKey( + '%s.%s.%s' % (key, i, tk.split(':')[1])) def removeKey(self, key): """ @@ -411,6 +454,7 @@ def removeKeysStartsWith(self, prefix): def keys(self): return list(self.dict.keys()) + class ConfigFileWithProfiles(ConfigFile): """ Store options in profiles as 'profileX.key=value' @@ -418,11 +462,13 @@ class ConfigFileWithProfiles(ConfigFile): Args: default_profile_name (str): default name of the first profile. """ - def __init__(self, default_profile_name = ''): + + def __init__(self, default_profile_name=''): ConfigFile.__init__(self) self.default_profile_name = default_profile_name self.current_profile_id = '1' + self.setCurrentProfile(self.current_profile_id) def load(self, filename): """ @@ -435,8 +481,7 @@ def load(self, filename): super(ConfigFileWithProfiles, self).load(filename) def append(self, filename): - """ - Load options from file and append them to current options. + """Load options from file and append them to current options. Args: filename (str): full path @@ -445,7 +490,9 @@ def append(self, filename): found = False profiles = self.profiles() + for profile_id in profiles: + if profile_id == self.current_profile_id: found = True break @@ -461,8 +508,8 @@ def append(self, filename): rename_keys.append(key) for old_key in rename_keys: - new_key = 'profile1.' + old_key[10 : ] - self.dict[new_key ] = self.dict[ old_key] + new_key = 'profile1.' + old_key[10:] + self.dict[new_key] = self.dict[old_key] del self.dict[old_key] if self.intValue('profiles.version') != 1: @@ -473,9 +520,9 @@ def profiles(self): List of all available profile IDs. Profile IDs are strings! Returns: - list: all available profile IDs as strings + list: List with strings of profile IDs as strings. """ - return self.strValue('profiles', '1').split(':') + return self.strValue(key='profiles', default='1').split(':') def profilesSortedByName(self): """ @@ -486,6 +533,7 @@ def profilesSortedByName(self): list: all available profile IDs as strings """ profiles_unsorted = self.profiles() + if len(profiles_unsorted) <= 1: return profiles_unsorted @@ -495,7 +543,8 @@ def profilesSortedByName(self): profiles_dict[self.profileName(profile_id).upper()] = profile_id # sort the dictionary by key (the profile name) - profiles_sorted = collections.OrderedDict(sorted(profiles_dict.items())) + profiles_sorted = collections.OrderedDict( + sorted(profiles_dict.items())) # return the names as a list return list(profiles_sorted.values()) @@ -521,37 +570,41 @@ def setCurrentProfile(self, profile_id): """ if isinstance(profile_id, int): profile_id = str(profile_id) + profiles = self.profiles() for i in profiles: + if i == profile_id: + + profile_name = self.profileName(profile_id) + self.current_profile_id = profile_id - logger.debug('change current profile: %s=%s' - % (profile_id, self.profileName(profile_id)), - self) - logger.changeProfile(profile_id) + logger.changeProfile(profile_id, profile_name) + logger.debug( + f'Change current profile to {profile_name}({profile_id})', + self) + return True return False def setCurrentProfileByName(self, name): """ - Change the current profile. + Change the current profile by a given name. Args: name (str): valid profile name Returns: - bool: ``True`` if successful + bool: ``True`` if successful """ - profiles = self.profiles() - for profile_id in profiles: + # Find the profile_id to this name... + for profile_id in self.profiles(): if self.profileName(profile_id) == name: - self.current_profile_id = profile_id - logger.debug('change current profile: %s' %name, self) - logger.changeProfile(profile_id) - return True + # ...and set current profile by this id. + return self.setCurrentProfile(profile_id) return False @@ -567,9 +620,10 @@ def profileExists(self, profile_id): """ if isinstance(profile_id, int): profile_id = str(profile_id) + return profile_id in self.profiles() - def profileExistsByName(self, name): + def profileExistsByName(self, name) -> bool: """ ``True`` if the profile exists. @@ -582,69 +636,74 @@ def profileExistsByName(self, name): profiles = self.profiles() for profile_id in profiles: + if self.profileName(profile_id) == name: return True return False - def profileName(self, profile_id = None): - """ - Name of the profile. + def profileName(self, profile_id=None): + """Name of the profile. Args: - profile_id (str, int): valid profile ID + profile_id (str, int): Valid profile ID Returns: - str: name of profile + str: Name of profile. """ if isinstance(profile_id, int): profile_id = str(profile_id) + if profile_id is None: profile_id = self.current_profile_id + if profile_id == '1': default = self.default_profile_name else: default = 'Profile %s' % profile_id + return self.profileStrValue('name', default, profile_id) - def addProfile(self, name): - """ - Add a new profile if the name is not already in use. + def addProfile(self, name: str) -> str | None: + """Add a new profile if the name is not already in use. Args: - name (str): new profile name + name (str): Profile name. Returns: - str: new profile ID + str: The new profile ID or None if profile with same name + already exists. """ - profiles = self.profiles() + if self.profileExistsByName(name): + self.notifyError( + _('Profile "{name}" already exists.').format(name=name)) - for profile_id in profiles: - if self.profileName(profile_id) == name: - self.notifyError(_('Profile "%s" already exists !') % name) - return None + return None - new_id = 1 - while True: - ok = True + profiles = self.profiles() - if str(new_id) in profiles: - ok = False + pid = self._next_unused_id() - if ok: - break + profiles.append(self._next_unused_id()) + self.setStrValue('profiles', ':'.join(profiles)) - new_id = new_id + 1 + self.setProfileStrValue('name', name, pid) - new_id = str(new_id) + return pid - profiles.append(new_id) - self.setStrValue('profiles', ':'.join(profiles)) + def _next_unused_id(self): + pid = 1 + existing_pids = self.profiles() - self.setProfileStrValue('name', name, new_id) - return new_id + while True: + if str(pid) in existing_pids: + pid += 1 + else: + break - def removeProfile(self, profile_id = None): + return str(pid) + + def removeProfile(self, profile_id=None): """ Remove profile and all its keys and values. @@ -656,23 +715,32 @@ def removeProfile(self, profile_id = None): """ if isinstance(profile_id, int): profile_id = str(profile_id) - if profile_id == None: + + if profile_id is None: profile_id = self.current_profile_id profiles = self.profiles() + if len(profiles) <= 1: - self.notifyError(_('You can\'t remove the last profile !')) + self.notifyError(_("The last profile cannot be removed.")) + return False found = False index = 0 + for profile in profiles: + if profile == profile_id: self.removeKeysStartsWith(self.profileKey('', profile_id)) + del profiles[index] + self.setStrValue('profiles', ':'.join(profiles)) found = True + break + index = index + 1 if not found: @@ -683,7 +751,7 @@ def removeProfile(self, profile_id = None): return True - def setProfileName(self, name, profile_id = None): + def setProfileName(self, name, profile_id=None): """ Change the name of the profile. @@ -696,38 +764,46 @@ def setProfileName(self, name, profile_id = None): """ if isinstance(profile_id, int): profile_id = str(profile_id) - if profile_id == None: + + if profile_id is None: profile_id = self.current_profile_id profiles = self.profiles() for profile in profiles: + if self.profileName(profile) == name: + if profile[0] != profile_id: - self.notifyError(_('Profile "%s" already exists !') % name) + self.notifyError(_( + 'Profile "{name}" already exists.').format(name=name)) + return False self.setProfileStrValue('name', name, profile_id) + return True - def profileKey(self, key, profile_id = None): + def profileKey(self, key, profile_id=None): """ Prefix for keys with profile. e.g. 'profile1.key' Args: - key (str): key name - profile_id (str, int): valid profile ID + key (str): Key identifier. + profile_id (str, int): Valid profile ID. Returns: - str: key with prefix 'profile1.key' + str: Key with prefix 'profile1.key' """ if isinstance(profile_id, int): profile_id = str(profile_id) + if profile_id is None: profile_id = self.current_profile_id + return 'profile' + profile_id + '.' + key - def removeProfileKey(self, key, profile_id = None): + def removeProfileKey(self, key, profile_id=None): """ Remove the key from profile. @@ -737,7 +813,7 @@ def removeProfileKey(self, key, profile_id = None): """ self.removeKey(self.profileKey(key, profile_id)) - def removeProfileKeysStartsWith(self, prefix, profile_id = None): + def removeProfileKeysStartsWith(self, prefix, profile_id=None): """ Remove the keys starting with prefix from profile. @@ -748,7 +824,7 @@ def removeProfileKeysStartsWith(self, prefix, profile_id = None): """ self.removeKeysStartsWith(self.profileKey(prefix, profile_id)) - def remapProfileKey(self, oldKey, newKey, profileId = None): + def remapProfileKey(self, oldKey, newKey, profileId=None): """ Remap profile keys to a new key name. @@ -760,7 +836,7 @@ def remapProfileKey(self, oldKey, newKey, profileId = None): self.remapKey(self.profileKey(oldKey, profileId), self.profileKey(newKey, profileId)) - def hasProfileKey(self, key, profile_id = None): + def hasProfileKey(self, key, profile_id=None): """ ``True`` if key is set in profile. @@ -773,26 +849,36 @@ def hasProfileKey(self, key, profile_id = None): """ return self.profileKey(key, profile_id) in self.dict - def profileStrValue(self, key, default = '', profile_id = None): + def profileStrValue(self, key, default='', profile_id=None): + """Return the value of ``key`` related to ``profile_id``. + + Returns: + str: The value. + """ return self.strValue(self.profileKey(key, profile_id), default) - def setProfileStrValue(self, key, value, profile_id = None): + def setProfileStrValue(self, key, value, profile_id=None): self.setStrValue(self.profileKey(key, profile_id), value) - def profileIntValue(self, key, default = 0, profile_id = None): + def profileIntValue(self, key, default=0, profile_id=None): return self.intValue(self.profileKey(key, profile_id), default) - def setProfileIntValue(self, key, value, profile_id = None): + def setProfileIntValue(self, key, value, profile_id=None): self.setIntValue(self.profileKey(key, profile_id), value) - def profileBoolValue(self, key, default = False, profile_id = None): + def profileBoolValue(self, key, default=False, profile_id=None): return self.boolValue(self.profileKey(key, profile_id), default) - def setProfileBoolValue(self, key, value, profile_id = None): + def setProfileBoolValue(self, key, value, profile_id=None): self.setBoolValue(self.profileKey(key, profile_id), value) - def profileListValue(self, key, type_key = 'str:value', default = [], profile_id = None): - return self.listValue(self.profileKey(key, profile_id), type_key, default) + def profileListValue(self, + key, + type_key='str:value', + default=[], + profile_id=None): + return self.listValue( + self.profileKey(key, profile_id), type_key, default) - def setProfileListValue(self, key, type_key, value, profile_id = None): + def setProfileListValue(self, key, type_key, value, profile_id=None): self.setListValue(self.profileKey(key, profile_id), type_key, value) diff --git a/common/configure b/common/configure index 87cdc48ca..33ec966b1 100755 --- a/common/configure +++ b/common/configure @@ -1,34 +1,46 @@ #!/bin/sh - -#clean up +# SPDX-FileCopyrightText: © 2009 Oprea Dan +# SPDX-FileCopyrightText: © 2013 Germar Reitze +# SPDX-FileCopyrightText: © 2022 Jürgen Altfeld +# SPDX-FileCopyrightText: © 2022 Christian Buhtz +# SPDX-FileCopyrightText: © 2023 Kian-Meng Ang +# SPDX-FileCopyrightText: © 2023 Fabio Fantoni +# SPDX-FileCopyrightText: © 2024 Tejas Guruswamy +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# This is the configuration file for the Read the Docs service. +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details. + +# Clean up if [ -e Makefile ]; then - rm Makefile; + rm Makefile; fi -#tmp files +# Tmp files MAKEFILE="$(mktemp)" UNINSTALL_FILES="$(mktemp)" UNINSTALL_DIRS="$(mktemp)" -#set default options -PYTHON="--python3" -FUSE_GROUP="--no-fuse-group" +# set default options +PYTHON="/usr/bin/python3" USR_BIN_FILES="backintime backintime-askpass" -FUSE_FILES="mount.py" usage () { echo "Usage:" - echo "$0 [--python | --python3], [--fuse-group | --no-fuse-group]" + echo "$0 [--python | --python3 | --python=PYTHON_BINARY]" echo "" echo "--python" echo "\tuse 'python' to start Python3" echo "--python3" echo "\tuse 'python3' to start Python3" - echo "--no-fuse-group" - echo "\tdo not check for 'fuse' group membership (if not necessary on destination platform)" - echo "--fuse-group" - echo "\tmake sure user is in group 'fuse' to be able to use fuse-based file-systems" + echo "--python=PYTHON_BINARY" + echo "\tuse PYTHON_BINARY to start Python3" } addInstallFiles () { @@ -57,7 +69,7 @@ addInstallFile () { addSymlink () { dst=$1 src=$2 - printf "\tln -s $dst \$(DEST)$src\n" >> ${MAKEFILE} + printf "\tln --symbolic --force $dst \$(DEST)$src\n" >> ${MAKEFILE} addUninstallFile "$src" } @@ -100,9 +112,9 @@ addUninstallDir () { } addComment () { - printf "\t#install $1\n" >> ${MAKEFILE} - printf "\t#uninstall files $1\n" >> ${UNINSTALL_FILES} - printf "\t#uninstall directory $1\n" >> ${UNINSTALL_DIRS} + printf "\t# Install $1\n" >> ${MAKEFILE} + printf "\t# Uninstall files $1\n" >> ${UNINSTALL_FILES} + printf "\t# Uninstall directory $1\n" >> ${UNINSTALL_DIRS} } addNewline () { @@ -115,50 +127,52 @@ onTravis () { [ "${TRAVIS}" = "true" ] } -#get commandline arguments +# Get commandline arguments unknown_args="" for arg in $*; do - case $arg in - --python | --python3) PYTHON=$arg;; - --fuse-group | --no-fuse-group) FUSE_GROUP=$arg;; - --help | -h) usage; exit 0;; - *) unknown_args="$unknown_args $arg";; - esac + case $arg in + --python=*) + PYTHON=$(echo $arg | cut -f2 -d'=') + ;; + --python3) + PYTHON="/usr/bin/python3" + ;; + --python) + PYTHON="/usr/bin/python" + ;; + --help | -h) usage; exit 0;; + *) unknown_args="$unknown_args $arg";; + esac done if [ -n "$unknown_args" ]; then - echo "Unknown Arguments: $unknown_args" + echo "Unknown Arguments: $unknown_args" +fi + +# Check if the python binary file exists +if [ ! -f "$PYTHON" ]; then + echo "Warning: \"${PYTHON}\" not found on this computer" +fi + +if [ -n "$(sed -e "s#^/usr/bin/python3\? #${PYTHON} #gw /dev/stdout" -i $USR_BIN_FILES)" ] +then + echo "Replacement of python path with \"${PYTHON}\" successful." +else + echo "WARNING: Replacement of python path with \"${PYTHON}\" FAILED. Maybe you ran configure more than once?" fi -#patch python command -#use 'python' or 'python3' to start Python Version 3.x -case $PYTHON in - --python) PYVERSION="" ;; - --python3) PYVERSION="3";; -esac -sed -e "s/^python3\? /python${PYVERSION} /g" \ - -e "s/^ssh-agent python3\? /ssh-agent python${PYVERSION} /g" \ - -i $USR_BIN_FILES - -#patch check for 'fuse' group -#Some distributions require user to be in group 'fuse' to use sshfs and encfs -case $FUSE_GROUP in - --fuse-group) CHECKFUSE="True" ;; - --no-fuse-group) CHECKFUSE="False";; -esac -sed -e "s/CHECK_FUSE_GROUP = \(True\|False\)/CHECK_FUSE_GROUP = ${CHECKFUSE}/" \ - -i $FUSE_FILES - -#check languages +# Check languages mos="" langs="" for langfile in `ls po/*.po`; do - lang=`echo $langfile | cut -d/ -f2 | cut -d. -f1` - mos="po/$lang.mo $mos" - langs="$lang $langs" + lang=`echo $langfile | cut -d/ -f2 | cut -d. -f1` + mos="po/$lang.mo $mos" + langs="$lang $langs" done -#start Makefile +# Start Makefile +printf ".PHONY: test test-v unittest unittest-v\n" >> ${MAKEFILE} + printf "LANGS=$langs\n\n" >> ${MAKEFILE} printf "PREFIX=/usr\n" >> ${MAKEFILE} @@ -166,16 +180,20 @@ printf "DEST=\$(DESTDIR)\$(PREFIX)\n\n" >> ${MAKEFILE} printf "all:\tbuild\n\n" >> ${MAKEFILE} -printf "build:\ttranslate compress\n\n" >> ${MAKEFILE} +printf "build:\ttranslate compress\n" >> ${MAKEFILE} printf "clean:\n" >> ${MAKEFILE} printf "\trm -f po/*.mo\n" >> ${MAKEFILE} -printf "\trm -f man/C/*.gz\n" >> ${MAKEFILE} +printf "\trm -f ../doc/manpages/*.gz\n" >> ${MAKEFILE} printf "\trm -f config-example-*.gz\n" >> ${MAKEFILE} -printf "\trm -rf doc-dev/_build/*\n\n" >> ${MAKEFILE} +printf "\trm -rf doc-dev/_build/*\n" >> ${MAKEFILE} +printf "\n" >> ${MAKEFILE} -#create install and uninstall target +# Create install and uninstall target printf "install:\tinstall_translations\n" >> ${MAKEFILE} +printf "\n\t# Inject version string into source files\n" >> ${MAKEFILE} +printf "\t(cd .. && ./updateversion.sh)\n\n" >> ${MAKEFILE} + addComment "python" addUninstallDir "/share/backintime/common/__pycache__" addUninstallFile "*.pyc" "/share/backintime/common/__pycache__" @@ -193,29 +211,33 @@ addNewline addComment "documentation" addInstallDir "/share/doc/backintime-common" -addInstallFile "../debian/copyright" "/share/doc/backintime-common" addInstallFile "../AUTHORS" "/share/doc/backintime-common" -addInstallFile "../LICENSE" "/share/doc/backintime-common" addInstallFile "../README.md" "/share/doc/backintime-common" -addInstallFile "../TRANSLATIONS" "/share/doc/backintime-common" -addInstallFile "../VERSION" "/share/doc/backintime-common" -addInstallFile "../CHANGES" "/share/doc/backintime-common" +addInstallFile "../FAQ.md" "/share/doc/backintime-common" +addInstallFile "../CHANGELOG.md" "/share/doc/backintime-common" +addInstallFile "../CHANGELOG.html" "/share/doc/backintime-common" +addInstallFile "../LICENSES.md" "/share/doc/backintime-common" +addInstallDir "/share/doc/backintime-common/LICENSES" +addInstallFiles "../LICENSES/*" "/share/doc/backintime-common/LICENSES" addNewline -addComment "config-examples" +addComment "config and user-callback examples" addInstallDir "/share/doc/backintime-common/examples" addInstallFile "config-example-local.gz" "/share/doc/backintime-common/examples" addInstallFile "config-example-ssh.gz" "/share/doc/backintime-common/examples" +addInstallDir "/share/doc/backintime-common/user-callback-examples" +addInstallFiles "../doc/user-callback-examples/user-callback.*" "/share/doc/backintime-common/user-callback-examples" addUninstallDir "/share/doc/backintime-common" addUninstallDir "/share/doc" addNewline addComment "man" -addInstallDir "/share/man/man1" -addInstallFile "man/C/backintime.1.gz" "/share/man/man1" -addInstallFile "man/C/backintime-askpass.1.gz" "/share/man/man1" -addInstallFile "man/C/backintime-config.1.gz" "/share/man/man1" -addUninstallDir "/share/man" +addInstallDir "/share/man/man1" +addInstallFile "../doc/manpages/backintime.1.gz" "/share/man/man1" +addInstallFile "../doc/manpages/backintime-askpass.1.gz" "/share/man/man1" +addInstallDir "/share/man/man5" +addInstallFile "../doc/manpages/backintime-config.5.gz" "/share/man/man5" +addUninstallDir "/share/man" addNewline addComment "application" @@ -238,36 +260,43 @@ addSymlink "backintime" "/share/bash-completion/completions/backinti addUninstallDir "/share/bash-completion" addNewline -#compress +# compress printf "compress:\n" >> ${MAKEFILE} -printf "\t#man pages\n" >> ${MAKEFILE} -printf "\tfor i in \$\$(ls -1 man/C/); do case \$\$i in *.gz|*~) continue;; *) gzip -n --best -c man/C/\$\$i > man/C/\$\${i}.gz;; esac; done\n\n" >> ${MAKEFILE} +printf "\t# Man pages\n" >> ${MAKEFILE} +printf "\t@cd ./../doc/manpages && ./build_manpages.sh backintime.1.adoc\n" >> ${MAKEFILE} +printf "\t@cd ./../doc/manpages && ./build_manpages.sh backintime-askpass.1.adoc\n" >> ${MAKEFILE} +printf "\t@cd ./../doc/manpages && gzip --best --stdout backintime-config.5 > backintime-config.5.gz\n" >> ${MAKEFILE} +printf "\t@echo \"Compressed backintime-config.5 into backintime-config.5.gz\"\n" >> ${MAKEFILE} -printf "\t#config-examples\n" >> ${MAKEFILE} +printf "\n\t# Changelog\n" >> ${MAKEFILE} +printf "\t@pandoc ./../CHANGELOG.md --standalone --from markdown --to html --metadata title=\"Back In Time - CHANGELOG\" --output ./../CHANGELOG.html\n" >> ${MAKEFILE} +printf "\t@echo \"Converted CHANGELOG.md to CHANGELOG.html\"\n" >> ${MAKEFILE} + +printf "\n\t# Config-examples\n" >> ${MAKEFILE} printf "\tgzip -n --best -c config-example-local > config-example-local.gz\n" >> ${MAKEFILE} printf "\tgzip -n --best -c config-example-ssh > config-example-ssh.gz\n\n" >> ${MAKEFILE} -#translate +# translate printf "translate:\t$mos\n\n" >> ${MAKEFILE} for lang in $langs; do - printf "po/$lang.mo: po/$lang.po\n" >> ${MAKEFILE} - printf "\tmsgfmt -o po/$lang.mo po/$lang.po\n\n" >> ${MAKEFILE} + printf "po/$lang.mo: po/$lang.po\n" >> ${MAKEFILE} + printf "\tmsgfmt -o po/$lang.mo po/$lang.po\n\n" >> ${MAKEFILE} done -#common langs +# common langs printf "install_translations:\n" >> ${MAKEFILE} addComment "translations" for lang in $langs; do - addInstallDir "/share/locale/$lang/LC_MESSAGES" - addInstallFileRename "po/$lang.mo" "/share/locale/$lang/LC_MESSAGES/backintime.mo" - addUninstallDir "/share/locale/$lang" + addInstallDir "/share/locale/$lang/LC_MESSAGES" + addInstallFileRename "po/$lang.mo" "/share/locale/$lang/LC_MESSAGES/backintime.mo" + addUninstallDir "/share/locale/$lang" done addUninstallDir "/share/locale" addUninstallDir "/share" addNewline -#uninstall +# uninstall printf "uninstall:\tuninstall_files uninstall_dirs\n\n" >> ${MAKEFILE} printf "uninstall_files:\n" >> ${MAKEFILE} cat ${UNINSTALL_FILES} >> ${MAKEFILE} @@ -275,63 +304,37 @@ cat ${UNINSTALL_FILES} >> ${MAKEFILE} printf "uninstall_dirs:\n" >> ${MAKEFILE} cat ${UNINSTALL_DIRS} >> ${MAKEFILE} -#test -for i in "py.test-3" "py.test-3.6" "py.test-3.5" "py.test-3.4"; do - PYTEST=$(which $i 2>/dev/null) - if [ -n "${PYTEST}" ]; then - break - fi -done -COVERAGE=$(which coverage 2>/dev/null) -#use "coverage run" only on travis-ci.org and if it is available -#this will pass informations to coveralls.io. -#otherwise use "python", "python3" or if available "py.test-3" -if onTravis && [ -n "${COVERAGE}" ]; then - CMD="coverage run -p" -else - CMD="python${PYVERSION}" -fi - -printf "test:\tunittest\n\n" >> ${MAKEFILE} -printf "test-v:\tunittest-v\n\n" >> ${MAKEFILE} -for v in "" "-v"; do - #provide unittests with and without verbosity -v - printf "unittest${v}:\n" >> ${MAKEFILE} - if onTravis || [ -z "${PYTEST}" ]; then - #if running on travis-ci.org or if py.test-3 is not available - #call every test/test_*.py with $CMD from above - for i in $(ls -1 test/test_*.py); do - printf "\t${CMD} -m unittest ${v} -b $i\n" >> ${MAKEFILE} - done - else - #else just call py.test-3 which will find test/test_*.py by itself - #py.test-3 has a nicer output so this is prefered over simple python3 - printf "\t${PYTEST} ${v}\n" >> ${MAKEFILE} - fi - printf "\n" >> ${MAKEFILE} +for target in test test-v unittest unittest-v; do + printf "\n\n%s:\n" "$target" >> ${MAKEFILE} + printf "\t@echo \"Target '%s' not available anymore. " "$target">> ${MAKEFILE} + printf "Use a test runner of your own choice " >> ${MAKEFILE} + printf "(e.g. 'unittest' or 'pytest').\"" >> ${MAKEFILE} + printf "\n\t@false" >> ${MAKEFILE} done -printf "integrationtest:\n" >> ${MAKEFILE} -printf "\ttest/test.sh\n" >> ${MAKEFILE} - -#copy Makefile +# Copy Makefile mv ${MAKEFILE} Makefile chmod 644 Makefile -#clean up +# Clean up for i in "${UNINSTALL_FILES}" "${UNINSTALL_DIRS}"; do if [ -e "$i" ]; then rm "$i" fi done -#check python version -if [ $(python${PYVERSION} --version 2>&1 | grep -c "^Python 3") -ne 1 ]; then - printf "Warning: Wrong Python version.\n" - printf "Please make sure Python 3.x is used by adding '--python' or '--python3'.\n" +# check python version +PYTHON_VERSION_REQUIRED="3.9" +PYTHON_VERSION_CURRENT=$(${PYTHON} --version | tr --delete 'Python ') + +# Credits: https://unix.stackexchange.com/a/285928/136851 +if [ "$(printf '%s\n' "$PYTHON_VERSION_REQUIRED" "$PYTHON_VERSION_CURRENT" | sort -V | head -n1)" != "$PYTHON_VERSION_REQUIRED" ]; then + printf "Error: Wrong Python version ${PYTHON_VERSION_CURRENT}. " + printf "But minimal version ${PYTHON_VERSION_REQUIRED} required.\n" exit 1 fi + printf "All OK. Now run:\n" printf " make\n" printf " sudo make install\n" diff --git a/common/create-manpage-backintime-config.py b/common/create-manpage-backintime-config.py deleted file mode 100644 index 51b109672..000000000 --- a/common/create-manpage-backintime-config.py +++ /dev/null @@ -1,212 +0,0 @@ -# Back In Time -# Copyright (C) 2012-2021 Germar Reitze -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import re -import os -import sys -from time import strftime, gmtime - -PATH = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0])) - -CONFIG = os.path.join(PATH, 'config.py') -MAN = os.path.join(PATH, 'man/C/backintime-config.1') -with open(os.path.join(PATH, '../VERSION'), 'r') as f: - VERSION = f.read().strip('\n') -SORT = True #True = sort by alphabet; False = sort by line numbering - -c_list = re.compile(r'.*?self\.(?!set)((?:profile)?)(List)Value ?\( ?[\'"](.*?)[\'"], ?((?:\(.*\)|[^,]*)), ?[\'"]?([^\'",\)]*)[\'"]?') -c = re.compile(r'.*?self\.(?!set)((?:profile)?)(.*?)Value ?\( ?[\'"](.*?)[\'"] ?(%?[^,]*?), ?[\'"]?([^\'",\)]*)[\'"]?') -c_default = re.compile(r'(^DEFAULT[\w]*|CONFIG_VERSION)[\s]*= (.*)') - -HEADER = '''.TH backintime-config 1 "%s" "version %s" "USER COMMANDS" -.SH NAME -config \- BackInTime configuration files. -.SH SYNOPSIS -~/.config/backintime/config -.br -/etc/backintime/config -.SH DESCRIPTION -Back In Time was developed as pure GUI program and so most functions are only -useable with backintime-qt. But it is possible to use -Back In Time e.g. on a headless server. You have to create the configuration file -(~/.config/backintime/config) manually. Look inside /usr/share/doc/backintime\-common/examples/ for examples. -.PP -The configuration file has the following format: -.br -keyword=arguments -.PP -Arguments don't need to be quoted. All characters are allowed except '='. -.PP -Run 'backintime check-config' to verify the configfile, create the snapshot folder and crontab entries. -.SH POSSIBLE KEYWORDS -''' % (strftime('%b %Y', gmtime()), VERSION) - -FOOTER = '''.SH SEE ALSO -backintime, backintime-qt. -.PP -Back In Time also has a website: https://github.com/bit-team/backintime -.SH AUTHOR -This manual page was written by BIT Team(). -''' - -INSTANCE = 'instance' -NAME = 'name' -VALUES = 'values' -DEFAULT = 'default' -COMMENT = 'comment' -REFERENCE = 'reference' -LINE = 'line' - -def output(instance = '', name = '', values = '', default = '', comment = '', reference = '', line = 0): - if not default: - default = "''" - ret = '.IP "\\fI%s\\fR" 6\n' % name - ret += '.RS\n' - ret += 'Type: %-10sAllowed Values: %s\n' %(instance.lower(), values) - ret += '.br\n' - ret += '%s\n' % comment - ret += '.PP\n' - if SORT: - ret += 'Default: %s\n' % default - else: - ret += 'Default: %-18s %s line: %d\n' % (default, reference, line) - ret += '.RE\n' - return ret - -def select(a, b): - if a: - return a - return b - -def select_values(instance, values): - if values: - return values - if instance.lower() == 'bool': - return 'true|false' - if instance.lower() == 'str': - return 'text' - if instance.lower() == 'int': - return '0-99999' - -def process_line(d, key, profile, instance, name, var, default, commentline, values, force_var, force_default, replace_default, counter): - #Ignore commentlines with #?! and 'config.version' - comment = None - if not commentline.startswith('!') and not key in d: - d[key] = {} - commentline = commentline.split(';') - try: - comment = commentline[0] - values = commentline[1] - force_default = commentline[2] - force_var = commentline[3] - except IndexError: - pass - - if default.startswith('self.') and default[5:] in replace_default: - default = replace_default[default[5:]] - - if isinstance(force_default, str) and force_default.startswith('self.') and force_default[5:] in replace_default: - force_default = replace_default[force_default[5:]] - - if instance.lower() == 'bool': - default = default.lower() - d[key][INSTANCE] = instance - d[key][NAME] = re.sub(r'%[\S]', '<%s>' % select(force_var, var).upper(), name) - d[key][VALUES] = select_values(instance, values) - d[key][DEFAULT] = select(force_default, default) - d[key][COMMENT] = re.sub(r'\\n', '\n.br\n', comment) - d[key][REFERENCE] = 'config.py' - d[key][LINE] = counter - -def main(): - replace_default = {} - d = {} - d['profiles.version'] = {INSTANCE : 'int', - NAME : 'profiles.version', - VALUES : '1', - DEFAULT : '1', - COMMENT : 'Internal version of profiles config.', - REFERENCE : 'configfile.py', - LINE : 419} - d['profiles'] = {INSTANCE : 'str', - NAME : 'profiles', - VALUES : 'int separated by colon (e.g. 1:3:4)', - DEFAULT : '1', - COMMENT : 'All active Profiles ( in profile.snapshots...).', - REFERENCE : 'configfile.py', - LINE : 472} - d['profile.name'] = {INSTANCE : 'str', - NAME : 'profile.name', - VALUES : 'text', - DEFAULT : 'Main profile', - COMMENT : 'Name of this profile.', - REFERENCE : 'configfile.py', - LINE : 704} - with open(CONFIG, 'r') as f: - commentline = '' - values = force_var = force_default = instance = name = var = default = None - for counter, line in enumerate(f, 1): - line = line.lstrip() - m_default = c_default.match(line) - if m_default: - replace_default[m_default.group(1)] = m_default.group(2).replace('\\$', '\\\$') - continue - if line.startswith('#?'): - if commentline and not ';' in commentline and not commentline.endswith('\\n'): - commentline += ' ' - commentline += line.lstrip('#?').rstrip('\n') - continue - if line.startswith('#'): - commentline = '' - continue - # m = c_list_tuple.match(line) - # if not m: - m = c_list.match(line) - if not m: - m = c.match(line) - if m: - profile, instance, name, var, default = m.groups() - if profile == 'profile': - name = 'profile.' + name - var = var.lstrip('% ') - if instance.lower() == 'list': - type_key = [x.strip('"\'') for x in re.findall(r'["\'].*?["\']', var)] - commentline_split = commentline.split('::') - for i, tk in enumerate(type_key): - t, k = tk.split(':', maxsplit = 1) - process_line(d, key, profile, 'int', '%s.size' %name, var, '\-1', 'Quantity of %s. entries.' %name, values, force_var, force_default, replace_default, counter) - key = '%s.%s' %(name, k) - key = key.lower() - process_line(d, key, profile, t, '%s..%s' %(name, k), var, '', commentline_split[i], values, force_var, force_default, replace_default, counter) - else: - key = re.sub(r'%[\S]', var, name).lower() - process_line(d, key, profile, instance, name, var, default, commentline, values, force_var, force_default, replace_default, counter) - - values = force_var = force_default = instance = name = var = default = None - commentline = '' - - with open(MAN, 'w') as f: - f.write(HEADER) - if SORT: - s = lambda x: x - else: - s = lambda x: d[x][LINE] - f.write('\n'.join(output(**d[key]) for key in sorted(d, key = s))) - f.write(FOOTER) - -if __name__ == '__main__': - main() diff --git a/common/daemon.py b/common/daemon.py new file mode 100644 index 000000000..20dd60e0e --- /dev/null +++ b/common/daemon.py @@ -0,0 +1,258 @@ +# SPDX-FileCopyrightText: © 2007 Sander Marechal +# SPDX-FileCopyrightText: © 2016 Germar Reitze +# SPDX-FileCopyrightText: © 2025 Christian Buhtz +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory or +# go to +# and . +"""A generic daemon class. + + Original from: + http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python + Copyright © 2007 Sander Marechal + License CC0 or Public Domain + +Notes about the license (by buhtz, 2024-08-13): + + The linked blog article is licensed under CC BY-SA 3.0 which is not + compatible with GPLv2 used by Back In Time. But the original author + clarified that the code used in the blog article is public domain. + +See this original email. + + Date: Tue, 13 Aug 2024 14:19:48 +0200 + Subject: Re: Your Daemon code in Back In Time + Message-ID: <06084b4b-2293-4a28-a290-96fa4d309a8b@email.android.com> + In-Reply-To: <3d57067b590e271ce6f361ce4147ac08@posteo.de> + From: Sander Marechal + To: c.buhtz@posteo.jp + + Hello Christian, + + As far as I am concerned that daemon code is public domain. You can use it + under any license you want. There are only a few ways to start a daemon so + technically the code can't be copyrighted as far as I am concerned. That CC + license is just for my articles in general. + + Kind regards, + + -- + Sander Marechal +""" +import sys +import os +import io +import signal +import atexit +import errno +from time import sleep +import logger +from applicationinstance import ApplicationInstance + + +def _duplicate_fd(old_fd: str, new_fd: io.TextIOWrapper, mode: str = 'w' + ) -> None: + """Duplicate a file descriptor and close it after. + + Used to redirect stdin, stdout and stderr from daemonized threads. + + Args: + old_fd: Path to the old file (e.g. /dev/stdout). + new_fd (_io.TextIOWrapper): File object for the new file. + mode (str): Mode in which the old file should be opened. + """ + try: + # pylint: disable-next=unspecified-encoding + with open(file=old_fd, mode=mode, encoding=None) as fd: + os.dup2(fd.fileno(), new_fd.fileno()) + + except OSError as exc: + logger.error(f'Failed to redirect {old_fd}: {exc}') + + +class Daemon: + """A generic daemon class. + + Usage: subclass the Daemon class and override the run() method + """ + + def __init__(self, # pylint: disable=R0913,R0917 + pidfile=None, + stdin='/dev/null', + stdout='/dev/stdout', + stderr='/dev/null', + umask=0o022): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = pidfile + self.umask = umask + + if pidfile: + self.app_instance = ApplicationInstance( + pidfile, autoExit=False, flock=False) + + def daemonize(self): + """Converts the current process into a daemon. + + It is a process running in the background, sending a SIGTERM signal to + the current process. This is done via the UNIX double-fork magic, see + Stevens' 'Advanced Programming in the UNIX Environment' for details + (ISBN 0201563177) and this explanation: + https://stackoverflow.com/a/6011298 + """ + try: + pid = os.fork() + logger.debug(f'first fork pid: {pid}', self) + + if pid > 0: + # exit first parent + sys.exit(0) + + except OSError as exc: + logger.error(f'fork #1 failed: {exc.errno} ({exc})', self) + sys.exit(1) + + # Decouple from parent environment + # logger.debug('decouple from parent environment', self) + os.chdir('/') + os.setsid() + os.umask(self.umask) + + # Do second fork + try: + pid = os.fork() + logger.debug(f'second fork pid: {pid}', self) + + if pid > 0: + # exit from second parent + sys.exit(0) + + except OSError as exc: + logger.error(f'fork #2 failed: {exc.errno} ({exc})', self) + sys.exit(1) + + # redirect standard file descriptors + # logger.debug('redirect standard file descriptors', self) + + sys.stdout.flush() + sys.stderr.flush() + _duplicate_fd(self.stdin, sys.stdin, 'r') + _duplicate_fd(self.stdout, sys.stdout, 'w') + _duplicate_fd(self.stderr, sys.stderr, 'w') + + signal.signal(signal.SIGTERM, self.cleanup_handler) + + if self.pidfile: + atexit.register(self.app_instance.exitApplication) + + # write pidfile + # logger.debug('write pidfile', self) + self.app_instance.startApplication() + + def cleanup_handler(self, _signum, _frame): + """Handle process termination.""" + if self.pidfile: + self.app_instance.exitApplication() + + sys.exit(0) + + def start(self): + """Start the daemon.""" + + # Check for a pidfile to see if the daemon already runs + if self.pidfile and not self.app_instance.check(): + logger.error(f'pidfile {self.pidfile} already exists. ' + 'Daemon already running?', self) + sys.exit(1) + + # Start the daemon + self.daemonize() + self.run() + + def stop(self): + """Stop the daemon.""" + + if not self.pidfile: + logger.warning( + 'Unattended daemon can not be stopped. No PID file.', self) + return + + # Get the pid from the pidfile + pid = self.app_instance.readPidFile()[0] + + if not pid: + logger.error( + f'pidfile {self.pidfile} does not exist. Daemon not running?', + self) + return # not an error in a restart + + # Try killing the daemon process + try: + while True: # <-- Why? + os.kill(pid, signal.SIGTERM) + sleep(0.1) + + except OSError as err: + + if err.errno == errno.ESRCH: + # No such process + self.app_instance.exitApplication() + + else: + logger.error(f'Unable to stop process with pid {pid}: {err}', + self) + sys.exit(1) + + def restart(self): + """Restart the daemon.""" + self.stop() + self.start() + + def reload(self): + """Send SIGHUP signal to process.""" + if not self.pidfile: + logger.warning( + "Unattended daemon can't be reloaded. No PID file", self) + return + + # Get the pid from the pidfile + pid = self.app_instance.readPidFile()[0] + + if not pid: + logger.error(f'pidfile {self.pidfile} does not exist. ' + 'Daemon not running?', self) + return + + # Try killing the daemon process + try: + os.kill(pid, signal.SIGHUP) + + except OSError as err: + + if err.errno == errno.ESRCH: + # no such process + self.app_instance.exitApplication() + + else: + sys.stderr.write(str(err)) + sys.exit(1) + + def status(self): + """Return status.""" + if not self.pidfile: + logger.warning( + "Unattended daemon can't be checked. No PID file", self) + return False + + return not self.app_instance.check() + + def run(self): + """Override this method when subclass ``Daemon``. It will be called + after the process has been daemonized by ``start()`` or ``restart()``. + """ diff --git a/common/diagnostics.py b/common/diagnostics.py new file mode 100644 index 000000000..df98eb3f9 --- /dev/null +++ b/common/diagnostics.py @@ -0,0 +1,438 @@ +# SPDX-FileCopyrightText: © 2022 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Provides the ability to collect diagnostic information on Back In Time. + +These are version numbers of the dependent tools, environment variables, +paths, operating system and the like. This is used to enhance error reports +and to enrich them with the necessary information as uncomplicated as possible. +""" +import sys +import os +import itertools +from pathlib import Path +import pwd +import platform +import locale +import subprocess +import json +import re +import config +import tools +import version + + +def collect_minimal_diagnostics(): + """Collect minimal information about backintime and the operating system. + + Returns: + dict: A nested dictionary. + """ + return { + 'backintime': { + 'name': config.Config.APP_NAME, + 'version': version.__version__, + 'running-as-root': pwd.getpwuid(os.getuid()).pw_name == 'root', + }, + 'host-setup': { + 'OS': _get_os_release() + } + } + + +def collect_diagnostics(): + """Collect information about environment, versions of tools and + packages used by Back In Time. + + The information can be used e.g. for debugging and bug reports. + + Returns: + dict: A nested dictionary. + """ + result = collect_minimal_diagnostics() + + # === BACK IN TIME === + + # work-around: Instantiate to get the user-callback folder + # (should be singleton) + cfg = config.Config() + + result['backintime'].update({ + 'latest-config-version': config.Config.CONFIG_VERSION, + 'local-config-file': cfg._LOCAL_CONFIG_PATH, + 'local-config-file-found': Path(cfg._LOCAL_CONFIG_PATH).exists(), + 'global-config-file': cfg._GLOBAL_CONFIG_PATH, + 'global-config-file-found': Path(cfg._GLOBAL_CONFIG_PATH).exists(), + # 'distribution-package': str(distro_path), + 'started-from': str(Path(config.__file__).parent), + 'user-callback': cfg.takeSnapshotUserCallback(), + 'keyring-supported': tools.KEYRING_SUPPORTED + }) + + # Git repo + bit_root_path = Path(tools.as_backintime_path("")) + git_info = tools.get_git_repository_info(bit_root_path) + + if git_info: + + result['backintime']['git-project-root'] = str(bit_root_path) + + for key in git_info: + result['backintime'][f'git-{key}'] = git_info[key] + + # == HOST setup === + result['host-setup'].update({ + # Kernel & Architecture + 'platform': platform.platform(), + # OS Version (and maybe name) + 'system': f'{platform.system()} {platform.version()}' + }) + + # Display system (X11 or Wayland), desktop, etc + # $XDG_SESSION_TYPE doesn't catch all edge cases. + # See: https://unix.stackexchange.com/q/202891/136851 + for var in ['XDG_SESSION_TYPE', 'XDG_CURRENT_DESKTOP', 'DESKTOP_SESSION']: + result['host-setup'][var] = os.environ.get(var, '(not set)') + + # locale (system language etc) + # + # Implementation note: With env var "LC_ALL=C" getlocale() will return + # (None, None). + # This throws an error in "join()": + # TypeError: sequence item 0: expected str instance, NoneType found + my_locale = locale.getlocale() + if all(x is None for x in my_locale): + my_locale = ["(Unknown)"] + result['host-setup']['locale'] = ', '.join(my_locale) + + # PATH environment variable + result['host-setup']['PATH'] = os.environ.get('PATH', '($PATH unknown)') + + # === PYTHON setup === + python = ' '.join(( + platform.python_version(), + ' '.join(platform.python_build()), + platform.python_implementation(), + platform.python_compiler() + )) + + # Python branch and revision if available + branch = platform.python_branch() + if branch: + python = f'{python} branch: {branch}' + + rev = platform.python_revision() + if rev: + python = f'{python} rev: {rev}' + + python_executable = Path(sys.executable) + + # Python interpreter + result['python-setup'] = { + 'python': python, + 'python-executable': str(python_executable), + 'python-executable-symlink': python_executable.is_symlink(), + } + + # Real interpreter path if it is used via a symlink + if result['python-setup']['python-executable-symlink']: + result['python-setup']['python-executable-resolved'] \ + = str(python_executable.resolve()) + + result['python-setup']['sys.path'] = sys.path + + result['python-setup']['qt'] = _get_qt_information() + + # === EXTERN TOOL === + result['external-programs'] = {} + + # RSYNC environment variables + for var in ['RSYNC_OLD_ARGS', 'RSYNC_PROTECT_ARGS']: + result['external-programs'][var] = os.environ.get( + var, '(not set)') + + result['external-programs']['rsync'] = _get_rsync_info() + + # ssh + result['external-programs']['ssh'] = _get_extern_versions(['ssh', '-V']) + + # sshfs + result['external-programs']['sshfs'] \ + = _get_extern_versions(['sshfs', '-V'], r'SSHFS version (.*)\n') + + # EncFS + # Using "[Vv]" in the pattern because encfs does translate its output. + # e.g. In German it is "Version" in English "version". + result['external-programs']['encfs'] \ + = _get_extern_versions(['encfs'], r'Build: encfs [Vv]ersion (.*)\n') + + # Shell + SHELL_ERR_MSG = '($SHELL not exists)' + shell = os.environ.get('SHELL', SHELL_ERR_MSG) + result['external-programs']['shell'] = shell + + if shell != SHELL_ERR_MSG: + shell_version = _get_extern_versions([shell, '--version']) + result['external-programs']['shell-version'] \ + = shell_version.split('\n')[0] + + result = _replace_username_paths( + result=result, + username=pwd.getpwuid(os.getuid()).pw_name + ) + + return result + + +def _get_qt_information(): + """Collect Version and Theme information from Qt. + + If environment variable ``DISPLAY`` is set a temporary QApplication + instances is created. + """ + # pylint: disable=import-outside-toplevel + try: + import PyQt6.QtCore + import PyQt6.QtGui + import PyQt6.QtWidgets + except ImportError: + return '(Cannot import PyQt6)' + + # Themes + theme_info = {} + + if tools.checkXServer(): # TODO use tools.is_Qt_working() when stable + qapp = PyQt6.QtWidgets.QApplication.instance() + + if not qapp: + qapp = PyQt6.QtWidgets.QApplication([]) + clean_up_myself = True + else: + clean_up_myself = False + + theme_info = { + 'Theme': PyQt6.QtGui.QIcon.themeName(), + 'Theme Search Paths': PyQt6.QtGui.QIcon.themeSearchPaths(), + 'Fallback Theme': PyQt6.QtGui.QIcon.fallbackThemeName(), + 'Fallback Search Paths': PyQt6.QtGui.QIcon.fallbackSearchPaths() + } + + if clean_up_myself: + qapp.quit() + del qapp + + return { + 'Version': f'PyQt {PyQt6.QtCore.PYQT_VERSION_STR} ' + f'/ Qt {PyQt6.QtCore.QT_VERSION_STR}', + **theme_info + } + + +def _get_extern_versions(cmd, + pattern=None, + try_json=False, + error_pattern=None): + """Get the version of an external tools using :class:`subprocess.Popen`. + + Args: + cmd (list[str]): Commandline arguments that will be passed + to ``Popen()``. + pattern (str) : A regex pattern to extract the version string from the + commands output. + try_json (bool): Interpret the output as json first + (default: ``False``). + If it could be parsed the result is a dict + error_pattern (str): Regex pattern to identify a message in the output + that indicates an error. + + Returns: + Version information as :obj:`str` or :obj:`dict`. + The latter is used if the + ``cmd`` requested offer its information in JSON format. + ``None`` if the error_pattern did match (to indicate an error). + """ + + try: + # as context manager to prevent ResourceWarning's + with subprocess.Popen(cmd, + env={'LC_ALL': 'C'}, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) as proc: + error_output = proc.stderr.read() + std_output = proc.stdout.read() + + except FileNotFoundError: + result = f'(no {cmd[0]})' + + else: + # Check for errors + if error_pattern: + match_result = re.findall(error_pattern, error_output) + + if match_result: + return None + + # some tools use "stderr" for version info + result = std_output if std_output else error_output + + # Expect JSON string + if try_json: + + try: + result = json.loads(result) + + except json.decoder.JSONDecodeError: + # Wasn't a json. Try regex in the next block. + pass + + else: + return result # as JSON + + # extract version string + if pattern: + result = re.findall(pattern, result)[0] + + return result.strip() # as string + + +def _get_rsync_info(): + """Collect infos about rsync. + + Returns: + dict: Collected info + """ + # rsync + # rsync >= 3.2.7: -VV return a json + # rsync <= 3.2.6 and > (somewhere near) 3.1.3: -VV return the same as -V + # rsync <= (somewhere near) 3.1.3: -VV doesn't exists + # rsync == 3.1.3 (Ubuntu 20 LTS) doesn't even know '-V' + + # This works when rsync understands -VV and returns json or human readable + info = _get_extern_versions( + ['rsync', '-VV'], + r'rsync version (.*) protocol version', + try_json=True, + error_pattern=r'unknown option' + ) + + # When -VV was unknown use -V and parse the human readable output + if not info: + # try the old way + info = _get_extern_versions( + ['rsync', '--version'], + r'rsync version (.*) protocol version' + ) + + elif isinstance(info, dict): + # Rsync (>= 3.2.7) provide its information in JSON format. + # Remove some irrelevant information. + for key in ['program', 'copyright', 'url', 'license', 'caveat']: + try: + del info[key] + except KeyError: + pass + + # Reduce use of vertical space with transforming lists and dicts into + # strings. + for key in ['daemon_auth_list', 'compress_list', 'checksum_list', + 'optimizations', 'capabilities']: + if isinstance(info[key], list): + info[key] = ', '.join(info[key]) + elif isinstance(info[key], dict): + info[key] = '; '.join( + f'{k}: {v}' for k, v in info[key].items()) + + return info + + +def _get_os_release(): + """Try to get the name and version of the operating system used. + + First it extract infos from the file ``/etc/os-release``. Because not all + GNU/Linux distributions follow the standards it will also look for + alternative release files (pattern: ``/etc/*release``). + See http://linuxmafia.com/faq/Admin/release-files.html for examples. + + Returns: + A string with the name of the operating system, e.g. "Debian + GNU/Linux 11 (bullseye)" or a dictionary if alternative release + files where found. + """ + + def _get_pretty_name_or_content(fp): + """Return value of PRETTY_NAME from a release file or return the whole + file content.""" + + # Read content from file + try: + with fp.open('r') as handle: + content = handle.read() + + except FileNotFoundError: + return f'({fp.name} file not found)' + + # Try to extract the pretty name + try: + return re.findall('PRETTY_NAME=\"(.*)\"', content)[0] + + except IndexError: + # Return full content when no PRETTY_NAME was found + return content + + etc_path = Path('/etc') + os_files = list(filter(lambda p: p.is_file(), + itertools.chain( + etc_path.glob('*release*'), + etc_path.glob('*version*')) + )) + + # "os-release" is standard and should be on top of the list + fp_osrelease = etc_path / 'os-release' + try: + os_files.remove(fp_osrelease) + except ValueError: + pass + else: + os_files = [fp_osrelease] + os_files + + # each release/version file found + osrelease = {str(fp): _get_pretty_name_or_content(fp) for fp in os_files} + + # No alternative release files found + if len(osrelease) == 1: + return osrelease[str(fp_osrelease)] + + return osrelease + + +def _replace_username_paths(result, username): + """User's real ``HOME`` path and login name are replaced with surrogtes. + + This is because of security reasons. + + Args: + result (dict): Dict possibly containing the username and its home + path. + username (str): The user's real login name to look for. + + Returns: + A dictionary with replacements. + """ + + # Replace home folder user names with this dummy name + # for privacy reasons + USER_REPLACED = 'UsernameReplaced' + + # JSON to string + result = json.dumps(result) + + result = result.replace(f'/home/{username}', f'/home/{USER_REPLACED}') + result = result.replace(f'~/{username}', f'~/{USER_REPLACED}') + + # string to JSON + return json.loads(result) diff --git a/common/doc-dev/REUSE.toml b/common/doc-dev/REUSE.toml new file mode 100644 index 000000000..d5638ccb9 --- /dev/null +++ b/common/doc-dev/REUSE.toml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: © 2024 Back In Time Team +# +# SPDX-License-Identifier: CC0-1.0 +# +# This file is released under Creative Commons Zero 1.0 (CC0-1.0) and part of +# the program "Back In Time". The program as a whole is released under GNU +# General Public License v2 or any later version (GPL-2.0-or-later). +# See LICENSES directory or go to +# and . + +# See https://reuse.software/faq/#bulk-license +version = 1 + +[[annotations]] +path = [ + "**/*.rst", + "Makefile", + "conf.py" +] +SPDX-License-Identifier = "GPL-2.0-or-later" +SPDX-FileCopyrightText = "© 2022 Back In Time" diff --git a/common/doc-dev/conf.py b/common/doc-dev/conf.py index 94d7e35d4..0693ea552 100644 --- a/common/doc-dev/conf.py +++ b/common/doc-dev/conf.py @@ -1,18 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# BackInTime documentation build configuration file, created by -# sphinx-quickstart on Sat Jan 9 00:04:35 2016. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - import sys import os @@ -22,23 +8,16 @@ #sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath(os.path.join(os.pardir))) - -#import config to solve race conditions between config an mount -import config +sys.path.insert(0, os.path.abspath(os.path.join(os.pardir, "plugins"))) # -- General configuration ------------------------------------------------ -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', + 'sphinx_rtd_theme' ] # Add any paths that contain templates here, relative to this directory. @@ -47,9 +26,6 @@ # The suffix of source filenames. source_suffix = '.rst' -# The encoding of source files. -#source_encoding = 'utf-8-sig' - # The master toctree document. master_doc = 'index' @@ -58,146 +34,57 @@ copyright = '2016, Germar Reitze' author = 'Germar Reitze' -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '1.1' +# Don't edit this variable. It is updated automatically by "updateversion.sh". +import version as version_module +version = version_module.__version__ # The full version, including alpha/beta/rc tags. -release = '1.1.12' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +release = version # '1.3.3-dev' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +autodoc_default_options = { + 'members': True, + 'member-order': 'bysource', + 'private-members': True, + 'undoc-members': True, + 'special-members': True, + 'exclude-members': '__weakref__,__dict__,__module__,__annotations__', +} # -- Intersphinx options -------------------------------------------------- -intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None)} +intersphinx_mapping = { + 'python': ('https://docs.python.org/3/', None), + # PyQt is not mappable because of a known issue. See + # https://riverbankcomputing.com/pipermail/pyqt/2013-March/032528.html +} # -- Napoleon include private members which have docstrings --------------- - napoleon_include_private_with_doc = True # -- Options for HTML output ---------------------------------------------- - # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'classic' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None +# html_theme = 'classic' +html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +html_last_updated_fmt = '%b %d, %Y, %H:%M (%Z)' # Output file base name for HTML help builder. htmlhelp_basename = 'BackInTimeDevDoc' - # -- Options for LaTeX output --------------------------------------------- latex_elements = { @@ -219,27 +106,6 @@ 'Germar Reitze', 'manual'), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples @@ -249,10 +115,6 @@ ['Germar Reitze'], 1) ] -# If true, show URL addresses after external links. -#man_show_urls = False - - # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples @@ -263,15 +125,3 @@ 'Germar Reitze', 'BackInTime', 'One line description of project.', 'Miscellaneous'), ] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/common/doc-dev/diagnostics.rst b/common/doc-dev/diagnostics.rst new file mode 100644 index 000000000..e78602a3b --- /dev/null +++ b/common/doc-dev/diagnostics.rst @@ -0,0 +1,7 @@ +diagnostics module +================== + +.. automodule:: diagnostics + :members: + :undoc-members: + :show-inheritance: diff --git a/common/doc-dev/driveinfo.rst b/common/doc-dev/driveinfo.rst deleted file mode 100644 index 18da8cb70..000000000 --- a/common/doc-dev/driveinfo.rst +++ /dev/null @@ -1,7 +0,0 @@ -driveinfo module -================ - -.. automodule:: driveinfo - :members: - :undoc-members: - :show-inheritance: diff --git a/common/doc-dev/dummytools.rst b/common/doc-dev/dummytools.rst deleted file mode 100644 index 0eee7995d..000000000 --- a/common/doc-dev/dummytools.rst +++ /dev/null @@ -1,7 +0,0 @@ -dummytools module -================= - -.. automodule:: dummytools - :members: - :undoc-members: - :show-inheritance: diff --git a/common/doc-dev/flock.rst b/common/doc-dev/flock.rst new file mode 100644 index 000000000..ae0f42354 --- /dev/null +++ b/common/doc-dev/flock.rst @@ -0,0 +1,7 @@ +flock module +============ + +.. automodule:: flock + :members: + :undoc-members: + :show-inheritance: diff --git a/common/doc-dev/index.rst b/common/doc-dev/index.rst index 6869cd6ba..20fc20a15 100644 --- a/common/doc-dev/index.rst +++ b/common/doc-dev/index.rst @@ -3,8 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to BackInTime's documentation! -====================================== +Welcome to Back In Time's documentation +======================================= Contents: @@ -13,6 +13,8 @@ Contents: modules.rst + plugins/modules.rst + Indices and tables ================== diff --git a/common/doc-dev/modules.rst b/common/doc-dev/modules.rst index eca7ac136..a6e2c3249 100644 --- a/common/doc-dev/modules.rst +++ b/common/doc-dev/modules.rst @@ -11,10 +11,10 @@ common cli config configfile - driveinfo - dummytools + diagnostics encfstools exceptions + flock guiapplicationinstance logger mount @@ -22,8 +22,9 @@ common password_ipc pluginmanager progress + schedule snapshotlog snapshots - sshMaxArg + ssh_max_arg sshtools tools diff --git a/common/doc-dev/plugins/modules.rst b/common/doc-dev/plugins/modules.rst new file mode 100644 index 000000000..7dc7b2609 --- /dev/null +++ b/common/doc-dev/plugins/modules.rst @@ -0,0 +1,7 @@ +plugins +======= + +.. toctree:: + :maxdepth: 4 + + usercallbackplugin diff --git a/common/doc-dev/plugins/usercallbackplugin.rst b/common/doc-dev/plugins/usercallbackplugin.rst new file mode 100644 index 000000000..98617e4f8 --- /dev/null +++ b/common/doc-dev/plugins/usercallbackplugin.rst @@ -0,0 +1,7 @@ +usercallbackplugin module +========================= + +.. automodule:: usercallbackplugin + :members: + :undoc-members: + :show-inheritance: diff --git a/common/doc-dev/schedule.rst b/common/doc-dev/schedule.rst new file mode 100644 index 000000000..ddd81e47a --- /dev/null +++ b/common/doc-dev/schedule.rst @@ -0,0 +1,7 @@ +schedule module +=============== + +.. automodule:: schedule + :members: + :undoc-members: + :show-inheritance: diff --git a/common/doc-dev/sshMaxArg.rst b/common/doc-dev/sshMaxArg.rst deleted file mode 100644 index 62f1e1375..000000000 --- a/common/doc-dev/sshMaxArg.rst +++ /dev/null @@ -1,7 +0,0 @@ -sshMaxArg module -================ - -.. automodule:: sshMaxArg - :members: - :undoc-members: - :show-inheritance: diff --git a/common/doc-dev/ssh_max_arg.rst b/common/doc-dev/ssh_max_arg.rst new file mode 100644 index 000000000..b58d0c839 --- /dev/null +++ b/common/doc-dev/ssh_max_arg.rst @@ -0,0 +1,7 @@ +ssh_max_arg module +================== + +.. automodule:: ssh_max_arg + :members: + :undoc-members: + :show-inheritance: diff --git a/common/driveinfo.py b/common/driveinfo.py deleted file mode 100644 index 9f4d5b195..000000000 --- a/common/driveinfo.py +++ /dev/null @@ -1,177 +0,0 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -import os -import configfile -import sys -import tools - -#TODO: rewrite and finally USE this -class DriveInfo(configfile.ConfigFile): - def __init__(self, path): - configfile.ConfigFile.__init__(self) - - self.path = path - self.load(self._get_driveinfo_file_()) - - dirty = False - - if sys.platform == 'win32': - #there is nothing to do - pass - else: - if not self.hasKey('hardlinks'): - self.setBoolValue('hardlinks', self._check_hardlinks_()) - dirty = True - - if not self.hasKey('permissions'): - self.setBoolValue('permissions', self._check_perms_()) - dirty = True - - if not self.hasKey('usergroup'): - self.setBoolValue('usergroup', self._check_usergroup_()) - dirty = True - - if dirty: - self.save(self._get_driveinfo_file_()) - - def support_hardlinks(self): - return self.boolValue('hardlinks', False) - - def support_permissions(self): - return self.boolValue('permissions', False) - - def support_usergroup(self): - return self.boolValue('usergroup', False) - - def _get_driveinfo_file_(self): - return os.path.join(self.path, 'driveinfo') - - def _check_hardlinks_(self): - tmp_path = os.path.join(self.path, 'driveinfo.tmp') - tools.makeDirs(tmp_path) - if not os.path.isdir(tmp_path): - return False - - file1_path = os.path.join(tmp_path, 'file1') - file2_path = os.path.join(tmp_path, 'file2') - - ret_val = False - - os.system("echo abc > \"%s\"" % file1_path) - os.system("ln \"%s\" \"%s\"" % (file1_path, file2_path)) - os.system("echo abc > \"%s\"" % file2_path) - - if os.path.exists(file1_path) and os.path.exists(file2_path): - try: - info1 = os.stat(file1_path) - info2 = os.stat(file2_path) - - if info1.st_size == info2.st_size: - ret_val = True - except: - pass - - os.system("rm -rf \"%s\"" % tmp_path) - return ret_val - - def _check_perms_for_file_(self, file_path, mode): - ret_val = False - - os.system("chmod %s \"%s\"" % (mode, file_path)) - try: - info = "%o" % os.stat(file_path).st_mode - info = info[-3 :] - if info == mode: - ret_val = True - except: - pass - - return ret_val - - def _check_perms_(self): - tmp_path = os.path.join(self.path, 'driveinfo.tmp') - tools.makeDirs(tmp_path) - if not os.path.isdir(tmp_path): - return False - - file_path = os.path.join(tmp_path, 'file') - os.system("echo abc > \"%s\"" % file_path) - if not os.path.isfile(file_path): - return False - - ret_val = False - - if self._check_perms_for_file_(file_path, '111'): - if self._check_perms_for_file_(file_path, '700'): - if self._check_perms_for_file_(file_path, '600'): - if self._check_perms_for_file_(file_path, '711'): - if self._check_perms_for_file_(file_path, '300'): - if self._check_perms_for_file_(file_path, '666'): - ret_val = True - - os.system("rm -rf \"%s\"" % tmp_path) - return ret_val - - def _check_usergroup_(self): - tmp_path = os.path.join(self.path, 'driveinfo.tmp') - tools.makeDirs(tmp_path) - if not os.path.isdir(tmp_path): - return False - - file_path = os.path.join(tmp_path, 'file') - os.system("echo abc > \"%s\"" % file_path) - if not os.path.isfile(file_path): - return False - - ret_val = False - - uid = os.getuid() - gid = os.getgid() - - try: - info = os.stat(file_path) - if info.st_uid == uid and info.st_gid == gid: - ret_val = True - except: - pass - - if ret_val and uid == 0: - #try to change the group - import grp - - #search for another group - new_gid = gid - new_name = '' - for group in grp.getgrall(): - if group.gr_gid != gid: - new_gid = group.gr_gid - new_name = group.gr_name - break - - if new_gid != gid: - os.system("chgrp %s \"%s\"" % (new_name, file_path)) - try: - info = os.stat(file_path) - if info.st_gid != new_gid: - ret_val = False - except: - ret_val = False - - os.system("rm -rf \"%s\"" % tmp_path) - return ret_val diff --git a/common/dummytools.py b/common/dummytools.py deleted file mode 100644 index 634a08d8d..000000000 --- a/common/dummytools.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (C) 2012-2021 Germar Reitze -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gettext - -import config -import mount - -_=gettext.gettext - -class Dummy(mount.MountControl): - """ - This is a template for mounting services. For simple mount services - all you need to do is: - - add your settings in qt/settingsdialog.py - - add settings in common/config.p - - modify a copy of this file - - Please use self.currentMountpoint as your local mountpoint. - This class inherit from mount.MountControl. All methodes from MountControl can - be used exactly like they were in this class. - Methodes from MountControl also can be overriden in here if you need - something different. - """ - def __init__(self, *args, **kwargs): - #init MountControl - super(Dummy, self).__init__(*args, **kwargs) - - self.all_kwargs = {} - - #First we need to map the settings. - #If is in kwargs (e.g. if this class is called with dummytools.Dummy( = ) - #this will map self. to kwargs[]; else self. = from config - #e.g. self.setattrKwargs(, , **kwargs) - self.setattrKwargs('user', self.config.get_dummy_user(self.profile_id), **kwargs) - self.setattrKwargs('host', self.config.get_dummy_host(self.profile_id), **kwargs) - self.setattrKwargs('port', self.config.get_dummy_port(self.profile_id), **kwargs) - self.setattrKwargs('password', self.config.password(parent, self.profile_id), store = False, **kwargs) - - self.setDefaultArgs() - - #if self.currentMountpoint is not the remote snapshot path you can specify - #a subfolder of self.currentMountpoint for the symlink - self.symlink_subfolder = None - - self.mountproc = 'dummy' - self.log_command = '%s: %s@%s' % (self.mode, self.user, self.host) - - def _mount(self): - """ - mount the service - """ - #implement your mountprocess here - pass - - def _umount(self): - """ - umount the service - """ - #implement your unmountprocess here - pass - - def preMountCheck(self, first_run = False): - """ - check what ever conditions must be given for the mount to be done successful - raise MountException(_('Error discription')) if service can not mount - return True if everything is okay - all pre|post_[u]mount_check can also be used to prepare things or clean up - """ - return True - - def postMountCheck(self): - """ - check if mount was successful - raise MountException(_('Error discription')) if not - """ - return True - - def preUmountCheck(self): - """ - check if service is safe to umount - raise MountException(_('Error discription')) if not - """ - return True - - def postUmountCheck(self): - """ - check if umount successful - raise MountException(_('Error discription')) if not - """ - return True diff --git a/common/encfstools.py b/common/encfstools.py index 1201fa625..756f59251 100644 --- a/common/encfstools.py +++ b/common/encfstools.py @@ -1,56 +1,57 @@ -# Copyright (C) 2012-2021 Germar Reitze, Taylor Raack +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Taylor Raack +# SPDX-FileCopyrightText: © 2025 David Wales (@daviewales) +# SPDX-FileCopyrightText: © 2025 Christian Buhtz # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os -import grp -import gettext import subprocess import re import shutil import tempfile from datetime import datetime -from distutils.version import StrictVersion - +from packaging.version import Version import config +import encode import password -import password_ipc +from password_ipc import TempPasswordThread import tools import sshtools import logger from mount import MountControl from exceptions import MountException, EncodeValueError -_=gettext.gettext + class EncFS_mount(MountControl): - """ - Mount encrypted paths with encfs. - """ + """Mount encrypted paths with encfs.""" + def __init__(self, *args, **kwargs): - #init MountControl + # logger.debug("EncFS_mount.init() :: {args=} {kwargs=}") # DEBUG + + # init MountControl super(EncFS_mount, self).__init__(*args, **kwargs) - self.setattrKwargs('path', self.config.localEncfsPath(self.profile_id), **kwargs) + # Workaround for some linters. + self.path = None + self.reverse = None + self.config_path = None + + self.setattrKwargs( + 'path', self.config.localEncfsPath(self.profile_id), **kwargs) + # logger.debug("EncFS_mount.init() :: {self.path=}") # DEBUG self.setattrKwargs('reverse', False, **kwargs) self.setattrKwargs('config_path', None, **kwargs) - self.setattrKwargs('password', None, store = False, **kwargs) + self.setattrKwargs('password', None, store=False, **kwargs) self.setattrKwargs('hash_id_1', None, **kwargs) self.setattrKwargs('hash_id_2', None, **kwargs) self.setDefaultArgs() + # pylint: disable=duplicate-code self.mountproc = 'encfs' self.log_command = '%s: %s' % (self.mode, self.path) self.symlink_subfolder = None @@ -59,40 +60,97 @@ def _mount(self): """ mount the service """ + if self.password is None: - self.password = self.config.password(self.parent, self.profile_id, self.mode) - logger.debug('Provide password through temp FIFO', self) - thread = password_ipc.TempPasswordThread(self.password) + self.password = self.config.password( + self.parent, self.profile_id, self.mode) + + # Dev note (2026-01, buhtz): + # Password flow overview: + # + # 1. Back In Time creates a TempPasswordThread and passes the password + # to it. + # 2. The thread creates a temporary FIFO and blocks while writing the + # password to it, waiting for a reader. + # 3. Back In Time starts encfs with "--extpass=backintime-askpass". + # 4. The FIFO path is passed via the environment variable ASKPASS_TEMP. + # 5. encfs invokes backintime-askpass as an external password helper. + # 6. backintime-askpass reads the FIFO path from ASKPASS_TEMP, opens + # the FIFO, reads the password, and writes it to stdout. + # 7. encfs reads the password from backintime-askpass's stdout. + # 8. After the read completes, the FIFO is removed and the thread + # exits. + # + # Result: + # The password is transferred exactly once, synchronously, via a FIFO, + # without appearing on the command line, in files, or in the process + # list. + # + # Reason: + # It is about security. It minimizes password lifetime and exposure. + # Password never appears in a shell context, is transffered only once. + + # Prepare the password-fifo-thread + thread = TempPasswordThread(self.password) env = self.env() env['ASKPASS_TEMP'] = thread.temp_file + + # Start thread and write password to FIFO with thread.starter(): + + # build encfs command and provide "backintime-askpass" as + # password helper encfs = [self.mountproc, '--extpass=backintime-askpass'] + if self.reverse: encfs += ['--reverse'] + if not self.isConfigured(): encfs += ['--standard'] + encfs += [self.path, self.currentMountpoint] - logger.debug('Call mount command: %s' - %' '.join(encfs), - self) - - proc = subprocess.Popen(encfs, env = env, - stdout = subprocess.PIPE, - stderr = subprocess.STDOUT, - universal_newlines = True) + logger.debug('Call mount command: ' + ' '.join(encfs), self) + + # Encfs ask backintime-askpass for the password. + # backintime-askpass will read the password from FIFO and provide + # it via return on stdout to the encfs process + proc = subprocess.Popen( + encfs, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True + ) output = proc.communicate()[0] + self.backupConfig() + if proc.returncode: - raise MountException(_('Can\'t mount \'%(command)s\':\n\n%(error)s') \ - % {'command': ' '.join(encfs), 'error': output}) + msg = _('Unable to mount "{command}"').format( + command=' '.join(encfs) + ) + raise MountException( + f'{msg}:\n\n{output}\n\nReturn code: {proc.returncode}' + ) - def preMountCheck(self, first_run = False): + def init_backend(self): + """Empty for Encfs because initialization happens implicit. + + Init happens in mount() via "--standard" switch on encfs if + self.isConfigured() is False. """ - check what ever conditions must be given for the mount + return + + def preMountCheck(self, first_run=False): + """Check what ever conditions must be given for the mount. + + Raises: Several exceptions. """ self.checkFuse() + if first_run: self.checkVersion() + return True def env(self): @@ -110,6 +168,7 @@ def configFile(self): return encfs config file """ f = '.encfs6.xml' + # pylint: disable=duplicate-code if self.config_path is None: cfg = os.path.join(self.path, f) else: @@ -122,86 +181,127 @@ def isConfigured(self): ask for password confirmation. _mount will then create a new config """ cfg = self.configFile() + if os.path.isfile(cfg): - logger.debug('Found encfs config in %s' - %cfg, self) + logger.debug(f'Found EncFS config in {cfg}', self) return True - else: - logger.debug('No encfs config in %s' - %cfg, self) - msg = _('Config for encrypted folder not found.') - if not self.tmp_mount: - raise MountException(msg) - else: - if not self.config.askQuestion(msg + _('\nCreate a new encrypted folder?')): - raise MountException(_('Cancel')) - else: - pw = password.Password(self.config) - password_confirm = pw.passwordFromUser(self.parent, prompt = _('Please confirm password')) - if self.password == password_confirm: - return False - else: - raise MountException(_('Password doesn\'t match')) + + logger.debug(f'No EncFS config in {cfg}', self) + msg = _('Configuration for the encrypted directory not found.') + + if not self.tmp_mount: + raise MountException(msg) + + question = '{}\n{}'.format( + msg, + _('Create a new encrypted directory?') + ) + + if not self.config.askQuestion(question): + # TODO + # This string can appear in a "critical" message dialog. + # Let us know the steps to reproduce that behavior. + raise MountException(_('Cancel')) + + pw = password.Password(self.config) + password_confirm = pw.passwordFromUser( + self.parent, + prompt=_('Please re-enter the EncFS password to confirm.')) + + if self.password == password_confirm: + return False + + raise MountException( + _('The EncFS passwords do not match.')) def checkVersion(self): - """ - check encfs version. + """Check encfs version. 1.7.2 had a bug with --reverse that will create corrupt files + + Dev note (buhtz, 2025-06): EncFS itself is scheduled for removal. + + Dev note (buhtz, 2024-05): Looking at upstream it seems that the 1.7.2 + release was widthdrawn. The release before and after are from the year + 2010. In consequence this code is definitely out dated and a candidate + for removal. """ logger.debug('Check version', self) if self.reverse: - proc = subprocess.Popen(['encfs', '--version'], - stdout = subprocess.PIPE, - stderr = subprocess.STDOUT, - universal_newlines = True) + proc = subprocess.Popen( + [ + 'encfs', + '--version' + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True + ) + output = proc.communicate()[0] + m = re.search(r'(\d\.\d\.\d)', output) - if m and StrictVersion(m.group(1)) <= StrictVersion('1.7.2'): - logger.debug('Wrong encfs version %s' %m.group(1), self) - raise MountException(_('encfs version 1.7.2 and before has a bug with option --reverse. Please update encfs')) + if m and Version(m.group(1)) <= Version('1.7.2'): + logger.debug('Wrong encfs version %s' % m.group(1), self) + raise MountException( + 'encfs version 1.7.2 and before has a bug with ' + 'option --reverse. Please update encfs.') def backupConfig(self): - """ - create a backup of encfs config file into local config folder - so in cases of the config file get deleted or corrupt user can restore - it from there + """Create a backup of encfs config file into local config folder. + + In cases of the config file get deleted or corrupt user can restore + it from there. """ cfg = self.configFile() if not os.path.isfile(cfg): - logger.warning('No encfs config in %s. Skip backup of config file.' %cfg, self) + logger.warning( + f'No encfs config in {cfg}. Skip backup of config file.', self + ) return + backup_folder = self.config.encfsconfigBackupFolder(self.profile_id) tools.makeDirs(backup_folder) + old_backups = os.listdir(backup_folder) - old_backups.sort(reverse = True) + old_backups.sort(reverse=True) + if len(old_backups): last_backup = os.path.join(backup_folder, old_backups[0]) - #don't create a new backup if config hasn't changed + # Don't create a new backup if config hasn't changed if tools.md5sum(cfg) == \ tools.md5sum(last_backup): logger.debug('Encfs config did not change. Skip backup', self) return - new_backup_file = '.'.join((os.path.basename(cfg), datetime.now().strftime('%Y%m%d%H%M'))) + new_backup_file = '.'.join(( + os.path.basename(cfg), + datetime.now().strftime('%Y%m%d%H%M') + )) new_backup = os.path.join(backup_folder, new_backup_file) - logger.debug('Create backup of encfs config %s to %s' - %(cfg, new_backup), self) + logger.debug( + f'Create backup of encfs config {cfg} to {new_backup}', self + ) shutil.copy2(cfg, new_backup) + class EncFS_SSH(EncFS_mount): """ Mount encrypted remote path with sshfs and encfs. Mount / with encfs --reverse. rsync will then sync the encrypted view on / to the remote path """ - def __init__(self, cfg = None, profile_id = None, mode = None, parent = None,*args, **kwargs): - self.config = cfg - if self.config is None: - self.config = config.Config() - self.profile_id = profile_id - if self.profile_id is None: - self.profile_id = self.config.currentProfile() + + def __init__( + self, + cfg=None, + profile_id=None, + mode=None, + parent=None, + *args, **kwargs + ): + self.config = cfg or config.Config() + self.profile_id = profile_id or self.config.currentProfile() self.mode = mode if self.mode is None: self.mode = self.config.snapshotsMode(self.profile_id) @@ -210,8 +310,13 @@ def __init__(self, cfg = None, profile_id = None, mode = None, parent = None,*ar self.args = args self.kwargs = kwargs - self.ssh = sshtools.SSH(*self.args, symlink = False, **self.splitKwargs('ssh')) - self.rev_root = EncFS_mount(*self.args, symlink = False, **self.splitKwargs('encfs_reverse')) + self.ssh = sshtools.SSH( + *self.args, symlink=False, **self.splitKwargs('ssh') + ) + self.rev_root = EncFS_mount( + *self.args, symlink=False, **self.splitKwargs('encfs_reverse') + ) + super(EncFS_SSH, self).__init__(*self.args, **self.splitKwargs('encfs')) def mount(self, *args, **kwargs): @@ -221,113 +326,169 @@ def mount(self, *args, **kwargs): """ logger.debug('Mount sshfs', self) self.ssh.mount(*args, **kwargs) - #mount fsroot with encfs --reverse first. - #If the config does not exist already this will make sure - #the new created config works with --reverse + # mount fsroot with encfs --reverse first. + # If the config does not exist already this will make sure + # the new created config works with --reverse + if not os.path.isfile(self.configFile()): - #encfs >= 1.8.0 changed behavior when ENCFS6_CONFIG environ variable - #file does not exist. It will not create a new one anymore but just fail. - #As encfs would create the config in /.encfs6.xml (which will most likly fail) - #we need to mount a temp folder with reverse first and copy the config when done. - logger.debug('Mount temp folder with encfs --reverse to create a new encfs config', self) + # encfs >= 1.8.0 changed behavior when ENCFS6_CONFIG environ + # variable file does not exist. It will not create a new one + # anymore but just fail. As encfs would create the config in + # /.encfs6.xml (which will most likely fail) we need to mount a + # temp folder with reverse first and copy the config when done. + + # logger.debug( + # 'Mount temp directory with encfs --reverse to create a new ' + # 'encfs config', + # self + # ) + with tempfile.TemporaryDirectory() as src: tmp_kwargs = self.splitKwargs('encfs_reverse') tmp_kwargs['path'] = src tmp_kwargs['config_path'] = src - tmp_mount = EncFS_mount(*self.args, symlink = False, **tmp_kwargs) + + tmp_mount = EncFS_mount( + *self.args, symlink=False, **tmp_kwargs) tmp_mount.mount(*args, **kwargs) tmp_mount.umount() + cfg = tmp_mount.configFile() + if os.path.isfile(cfg): - logger.debug('Copy new encfs config %s to its original place %s' %(cfg, self.ssh.currentMountpoint), self) + logger.debug( + f'Copy new encfs config {cfg} to its original place ' + f'{self.ssh.currentMountpoint}', + self + ) shutil.copy2(cfg, self.ssh.currentMountpoint) + else: - logger.error('New encfs config %s not found' %cfg, self) - logger.debug('Mount local filesystem root with encfs --reverse', self) + logger.error(f'New encfs config {cfg} not found', self) + + # logger.debug('Mount local filesystem root with encfs --reverse', self) self.rev_root.mount(*args, **kwargs) - logger.debug('Mount encfs', self) + # logger.debug('Mount encfs', self) kwargs['check'] = False + ret = super(EncFS_SSH, self).mount(*args, **kwargs) + self.config.ENCODE = Encode(self) + return ret def umount(self, *args, **kwargs): + """Close 'encfsctl encode' process and set config.ENCODE back to the + dummy class. Call umount for encfs, encfs --reverse and sshfs """ - close 'encfsctl encode' process and set config.ENCODE back to the dummy class. - call umount for encfs, encfs --reverse and sshfs - """ + self.config.ENCODE.close() - self.config.ENCODE = Bounce() - logger.debug('Unmount encfs', self) + self.config.ENCODE = encode.Bounce() + + # logger.debug('Unmount encfs', self) + super(EncFS_SSH, self).umount(*args, **kwargs) - logger.debug('Unmount local filesystem root mount encfs --reverse', self) + # logger.debug('Unmount local filesystem root mount encfs --reverse', self) + self.rev_root.umount(*args, **kwargs) - logger.debug('Unmount sshfs', self) + # logger.debug('Unmount sshfs', self) + self.ssh.umount(*args, **kwargs) def preMountCheck(self, *args, **kwargs): + """Call preMountCheck for sshfs, encfs --reverse and encfs. """ - call preMountCheck for sshfs, encfs --reverse and encfs - """ - if self.ssh.preMountCheck(*args, **kwargs) and \ - self.rev_root.preMountCheck(*args, **kwargs) and \ - super(EncFS_SSH, self).preMountCheck(*args, **kwargs): - return True + if (self.ssh.preMountCheck(*args, **kwargs) + and self.rev_root.preMountCheck(*args, **kwargs) + and super(EncFS_SSH, self).preMountCheck(*args, **kwargs)): + + # Dev note (buhtz, 2024-09): Seems unnecessary. No one checks this + # return value. + return True def splitKwargs(self, mode): """ split all given arguments for the desired mount class """ d = self.kwargs.copy() - d['cfg'] = self.config + d['cfg'] = self.config d['profile_id'] = self.profile_id - d['mode'] = self.mode - d['parent'] = self.parent + d['mode'] = self.mode + d['parent'] = self.parent + if mode == 'ssh': if 'path' in d: d.pop('path') + if 'ssh_path' in d: d['path'] = d.pop('ssh_path') + if 'ssh_password' in d: d['password'] = d.pop('ssh_password') else: - d['password'] = self.config.password(parent = self.parent, profile_id = self.profile_id, mode = self.mode) + d['password'] = self.config.password( + parent=self.parent, + profile_id=self.profile_id, + mode=self.mode + ) + if 'hash_id' in d: d.pop('hash_id') + if 'hash_id_2' in d: d['hash_id'] = d['hash_id_2'] + return d elif mode == 'encfs': d['path'] = self.ssh.currentMountpoint d['hash_id_1'] = self.rev_root.hash_id d['hash_id_2'] = self.ssh.hash_id + if 'encfs_password' in d: d['password'] = d.pop('encfs_password') + else: - d['password'] = self.config.password(parent = self.parent, profile_id = self.profile_id, mode = self.mode, pw_id = 2) + d['password'] = self.config.password( + parent=self.parent, + profile_id=self.profile_id, + mode=self.mode, + pw_id=2 + ) + return d elif mode == 'encfs_reverse': d['reverse'] = True d['path'] = '/' d['config_path'] = self.ssh.currentMountpoint + if 'encfs_password' in d: d['password'] = d.pop('encfs_password') else: - d['password'] = self.config.password(parent = self.parent, profile_id = self.profile_id, mode = self.mode, pw_id = 2) + d['password'] = self.config.password( + parent=self.parent, + profile_id=self.profile_id, + mode=self.mode, + pw_id=2 + ) + if 'hash_id' in d: d.pop('hash_id') + if 'hash_id_1' in d: d['hash_id'] = d['hash_id_1'] + return d -class Encode(object): + +class Encode: """ encode path with encfsctl. - ENCFS_SSH will replace config.ENCODE whit this + ENCFS_SSH will replace config.ENCODE with this """ + def __init__(self, encfs): self.encfs = encfs self.password = self.encfs.password @@ -338,7 +499,7 @@ def __init__(self, encfs): if not self.remote_path[-1] == os.sep: self.remote_path += os.sep - #precompile some regular expressions + # Precompile some regular expressions self.re_asterisk = re.compile(r'\*') self.re_separate_asterisk = re.compile(r'(.*?)(\*+)(.*)') @@ -349,19 +510,20 @@ def startProcess(self): """ start 'encfsctl encode' process in pipe mode. """ - thread = password_ipc.TempPasswordThread(self.password) + thread = TempPasswordThread(self.password) env = self.encfs.env() env['ASKPASS_TEMP'] = thread.temp_file with thread.starter(): - logger.debug('start \'encfsctl encode\' process', self) encfsctl = ['encfsctl', 'encode', '--extpass=backintime-askpass', '/'] - logger.debug('Call command: %s' - %' '.join(encfsctl), - self) - self.p = subprocess.Popen(encfsctl, env = env, bufsize = 0, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - universal_newlines = True) + logger.debug(f'Call command: {encfsctl}', self) + self.p = subprocess.Popen( + encfsctl, + env=env, + bufsize=0, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + universal_newlines=True + ) def path(self, path): """ @@ -369,16 +531,22 @@ def path(self, path): """ if not 'p' in vars(self): self.startProcess() + if not self.p.returncode is None: - logger.warning('\'encfsctl encode\' process terminated. Restarting.', self) + logger.warning( + "'encfsctl encode' process terminated. Restarting.", self + ) del self.p + self.startProcess() + self.p.stdin.write(path + '\n') ret = self.p.stdout.readline().strip('\n') + if not len(ret) and len(path): - logger.debug('Failed to encode %s. Got empty string' - %path, self) + logger.debug(f'Failed to encode {path}. Got empty string', self) raise EncodeValueError() + return ret def exclude(self, path): @@ -393,35 +561,48 @@ def exclude(self, path): enc = '' m = self.re_asterisk.search(path) + if not m is None: path_ = path[:] + while True: - #search for foo/*, foo/*/bar, */bar or **/bar - #but not foo* or foo/*bar + # Search for foo/*, foo/*/bar, */bar or **/bar + # but not foo* or foo/*bar m = self.re_separate_asterisk.search(path_) + if m is None: return None + if m.group(1): if not m.group(1).endswith(os.sep): return None enc = os.path.join(enc, self.path(m.group(1))) + enc = os.path.join(enc, m.group(2)) + if m.group(3): if not m.group(3).startswith(os.sep): return None + m1 = self.re_asterisk.search(m.group(3)) + if m1 is None: enc = os.path.join(enc, self.path(m.group(3))) break + else: path_ = m.group(3) continue + else: break + else: enc = self.path(path) + if os.path.isabs(path): return os.path.join(os.sep, enc) + return enc def include(self, path): @@ -435,6 +616,7 @@ def remote(self, path): encode the path on remote host starting from backintime/host/user/... """ enc_path = self.path(path[len(self.remote_path):]) + return os.path.join(self.remote_path, enc_path) def close(self): @@ -442,66 +624,56 @@ def close(self): stop encfsctl process """ if 'p' in vars(self) and self.p.returncode is None: - logger.debug('stop \'encfsctl encode\' process', self) + logger.debug("stop 'encfsctl encode' process", self) self.p.communicate() -class Bounce(object): - """ - Dummy class that will simply return all input. - This is the standard for config.ENCODE - """ - def __init__(self): - self.chroot = os.sep - - def path(self, path): - return path - - def exclude(self, path): - return path - - def include(self, path): - return path - - def remote(self, path): - return path - - def close(self): - pass -class Decode(object): +class Decode: """ decode path with encfsctl. """ - def __init__(self, cfg, string = True): + + def __init__(self, cfg, string=True): self.config = cfg self.mode = cfg.snapshotsMode() + if self.mode == 'local_encfs': - self.password = cfg.password(pw_id = 1) + self.password = cfg.password(pw_id=1) + elif self.mode == 'ssh_encfs': - self.password = cfg.password(pw_id = 2) + self.password = cfg.password(pw_id=2) + self.encfs = cfg.SNAPSHOT_MODES[self.mode][0](cfg) self.remote_path = cfg.sshSnapshotsPath() + if not self.remote_path: self.remote_path = './' + if not self.remote_path[-1] == os.sep: self.remote_path += os.sep - #german translation changed from Snapshot to Schnappschuss. - #catch both variants otherwise old logs wouldn't get decoded. - takeSnapshot = _('Take snapshot').replace('Schnappschuss', '(?:Schnappschuss|Snapshot)') + # German translation changed from Snapshot to Schnappschuss. + # Catch both variants otherwise old logs wouldn't get decoded. + # Warning (2023-11): Do not modify the source string. + # See #1559 for details. + takeSnapshot = _('Take snapshot') \ + .replace('Schnappschuss', '(?:Schnappschuss|Snapshot)') + + host, _post, user, path, _cipher = cfg.sshHostUserPortPathCipher() - #precompile some regular expressions - host, port, user, path, cipher = cfg.sshHostUserPortPathCipher() - #replace: --exclude"" or --include"" - self.re_include_exclude = re.compile(r'(--(?:ex|in)clude=")(.*?)(")') + # replace: --exclude"" or --include"" + self.re_include_exclude = re.compile( + r'(--(?:ex|in)clude=")(.*?)(")') # codespell-ignore - #replace: 'USER@HOST:"PATH"' - self.re_remote_path = re.compile(r'(\'%s@%s:"%s)(.*?)("\')' %(user, host, path)) + # replace: 'USER@HOST:"PATH"' + self.re_remote_path = re.compile( + r'(\'%s@%s:"%s)(.*?)("\')' % (user, host, path) + ) - #replace: --link-dest="../../" - self.re_link_dest = re.compile(r'(--link-dest="\.\./\.\./)(.*?)(")') + # replace: --link-dest="../../" + self.re_link_dest = re.compile(r'(--link-dest="\.\./\.\./)(.*?)(")') - #search for: [C] + # search for: [C] self.re_change = re.compile(r'(^\[C\] .{11} )(.*)') #search for: [I] Take snapshot (rsync: BACKINTIME: ) @@ -526,22 +698,24 @@ def __init__(self, cfg, string = True): pattern = [] pattern.append(r' rsync: readlink_stat\(".*?mountpoint/') pattern.append(r' rsync: send_files failed to open ".*?mountpoint/') + if self.remote_path == './': pattern.append(r' rsync: recv_generator: failed to stat "/home/[^/]*/') pattern.append(r' rsync: recv_generator: mkdir "/home/[^/]*/') else: pattern.append(r' rsync: recv_generator: failed to stat ".*?{}'.format(self.remote_path)) pattern.append(r' rsync: recv_generator: mkdir ".*?{}'.format(self.remote_path)) + pattern.append(r' rsync: .*?".*?mountpoint/') self.re_error = re.compile(r'(^(?:\[E\] )?Error:(?:%s))(.*?)(".*)' % '|'.join(pattern)) - #search for: [I] ssh USER@HOST cp -aRl "PATH"* "PATH" + # search for: [I] ssh USER@HOST cp -aRl "PATH"* "PATH" self.re_info_cp= re.compile(r'(^\[I\] .*? cp -aRl "%s/)(.*?)("\* "%s/)(.*?)(")' % (path, path)) - #search for all chars except * + # search for all chars except * self.re_all_except_asterisk = re.compile(r'[^\*]+') - #search for: -> + # search for: -> self.re_all_except_arrow = re.compile(r'(.*?)((?: [-=]> )+)(.*)') #skip: [I] Take snapshot (rsync: sending incremental file list) @@ -560,10 +734,8 @@ def __init__(self, cfg, string = True): self.re_skip = re.compile(r'^(?:\[I\] )?%s \(rsync: (%s)' % (takeSnapshot, '|'.join(pattern))) self.string = string - if string: - self.newline = '\n' - else: - self.newline = b'\n' + + self.newline = '\n' if string else b'\n' def __del__(self): self.close() @@ -572,44 +744,59 @@ def startProcess(self): """ start 'encfsctl decode' process in pipe mode. """ - thread = password_ipc.TempPasswordThread(self.password) + thread = TempPasswordThread(self.password) env = os.environ.copy() env['ASKPASS_TEMP'] = thread.temp_file + with thread.starter(): - logger.debug('start \'encfsctl decode\' process', self) - encfsctl = ['encfsctl', 'decode', '--extpass=backintime-askpass', self.encfs.path] - logger.debug('Call command: %s' - %' '.join(encfsctl), - self) - self.p = subprocess.Popen(encfsctl, env = env, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - universal_newlines = self.string, #return string (if True) or bytes - bufsize = 0) + encfsctl = [ + 'encfsctl', + 'decode', + '--extpass=backintime-askpass', + self.encfs.path + ] + logger.debug(f'Call command: {encfsctl}', self) + + self.p = subprocess.Popen( + encfsctl, + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + # return string (if True) or bytes + universal_newlines=self.string, + bufsize=0 + ) def path(self, path): """ - write crypted path to encfsctl stdin and read plain path from stdout - if stdout is empty (most likly because there was an error) return crypt path + write encrypted path to encfsctl stdin and read plain path from stdout + if stdout is empty (most likely because there was an error) return crypt path """ if self.string: assert isinstance(path, str), 'path is not str type: %s' % path else: assert isinstance(path, bytes), 'path is not bytes type: %s' % path + if not 'p' in vars(self): self.startProcess() + if not self.p.returncode is None: - logger.warning('\'encfsctl decode\' process terminated. Restarting.', self) + logger.warning( + "'encfsctl decode' process terminated. Restarting.", self + ) + del self.p self.startProcess() + self.p.stdin.write(path + self.newline) ret = self.p.stdout.readline() ret = ret.strip(self.newline) + if ret: return ret + return path - #TODO: rename this, 'list' is corrupting sphinx doc def list(self, list_): """ decode a list of paths @@ -617,37 +804,44 @@ def list(self, list_): output = [] for path in list_: output.append(self.path(path)) + return output def log(self, line): """ decode paths in takesnapshot.log """ - #rsync cmd + # rsync cmd if line.startswith('[I] rsync') or line.startswith('[I] nocache rsync'): line = self.re_include_exclude.sub(self.replace, line) line = self.re_remote_path.sub(self.replace, line) line = self.re_link_dest.sub(self.replace, line) return line - #[C] Change lines + + # [C] Change lines m = self.re_change.match(line) if not m is None: return m.group(1) + self.pathWithArrow(m.group(2)) - #[I] Information lines + + # [I] Information lines m = self.re_skip.match(line) if not m is None: return line + m = self.re_info.match(line) if not m is None: return m.group(1) + self.pathWithArrow(m.group(2)) + m.group(3) - #[E] Error lines + + # [E] Error lines m = self.re_error.match(line) if not m is None: return m.group(1) + self.path(m.group(2)) + m.group(3) - #cp cmd + + # cp cmd m = self.re_info_cp.match(line) if not m is None: return m.group(1) + self.path(m.group(2)) + m.group(3) + self.path(m.group(4)) + m.group(5) + return line def replace(self, m): @@ -655,8 +849,10 @@ def replace(self, m): return decoded string for re.sub """ decrypt = self.re_all_except_asterisk.sub(self.pathMatch, m.group(2)) + if os.path.isabs(m.group(2)): decrypt = os.path.join(os.sep, decrypt) + return m.group(1) + decrypt + m.group(3) def pathMatch(self, m): @@ -666,12 +862,14 @@ def pathMatch(self, m): return self.path(m.group(0)) def pathWithArrow(self, path): - """ - rsync print symlinks like 'dest -> src'. This will decode both and also normal paths + """rsync print symlinks like 'dest -> src'. This will decode both and + also normal paths """ m = self.re_all_except_arrow.match(path) + if not m is None: return self.path(m.group(1)) + m.group(2) + self.path(m.group(3)) + else: return self.path(path) @@ -683,6 +881,7 @@ def remote(self, path): remote_path = self.remote_path.encode() dec_path = self.path(path[len(remote_path):]) + return os.path.join(remote_path, dec_path) def close(self): diff --git a/common/encode.py b/common/encode.py new file mode 100644 index 000000000..b12539360 --- /dev/null +++ b/common/encode.py @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Taylor Raack +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Split from common/encfstools.py. + +"""Module related to encrypted backup profiles. +""" +import os + + +class Bounce: + """Dummy class that will simply return all input. + + This is the standard for config.ENCODE. + """ + + def __init__(self): + self.chroot = os.sep + + def path(self, path: str) -> str: + """The path.""" + return path + + def exclude(self, path: str) -> str: + """Exclude entry.""" + return path + + def include(self, path: str) -> str: + """Include entry.""" + return path + + def remote(self, path: str) -> str: + """Remote path.""" + return path + + def close(self) -> None: + """Nothing.""" diff --git a/common/exceptions.py b/common/exceptions.py index 1c70136ab..9c81b6140 100644 --- a/common/exceptions.py +++ b/common/exceptions.py @@ -1,45 +1,42 @@ -# Copyright (C) 2015-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2015-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . class BackInTimeException(Exception): pass + class MountException(BackInTimeException): pass + class NoPubKeyLogin(MountException): pass + class KnownHost(MountException): pass + class HashCollision(BackInTimeException): pass + class EncodeValueError(BackInTimeException): pass + class StopException(BackInTimeException): pass + class Timeout(BackInTimeException): pass -class LastSnapshotSymlink(BackInTimeException): - pass class InvalidChar(BackInTimeException): def __init__(self, msg): @@ -48,6 +45,7 @@ def __init__(self, msg): def __str__(self): return self.msg + class InvalidCmd(BackInTimeException): def __init__(self, msg): self.msg = msg @@ -55,6 +53,7 @@ def __init__(self, msg): def __str__(self): return self.msg + class LimitExceeded(BackInTimeException): def __init__(self, msg): self.msg = msg @@ -62,6 +61,7 @@ def __init__(self, msg): def __str__(self): return self.msg + class PermissionDeniedByPolicy(BackInTimeException): def __init__(self, msg): self.msg = msg diff --git a/common/flock.py b/common/flock.py new file mode 100644 index 000000000..14b1893c3 --- /dev/null +++ b/common/flock.py @@ -0,0 +1,195 @@ +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Manage file lock. + +Offer context managers to manage file lock (flock) files. +""" +import os +import fcntl +from pathlib import Path +import logger + + +class _FlockContext: + """Context manager to manage file locks (flock). + + It will be tried to establish a multi-user file lock; if not feasible a + single-user file lock will be used. It depends on the GNU/Linux + distribution used and the write permissions to the file lock locations in + the file system. + + Usage example :: + + class MyFlock(_FlockContext): + def __init__(self): + super().__init__('my.lock') + + with MyFlock(): + do_fancy_things() + + The following directories will be checked in sequence to determine if they + exist, if a file lock file exists within them, or if there are sufficient + permissions to create such a file within them. :: + + /run/lock + /var/lock + /run/user// + ~/.cache + + The first and second directory in that list is for multi-user file lock. + + To the experience of the developers on Debian-based distributions there is + no problem having a multi-user file lock. But on Arch-based distributions + only a user with root privileges is able to do it. Because of that on Arch + a single-user file lock is used by default until Back In Time is started + once as root. + """ + + def __init__(self, + filename: str, + disable: bool = False): + """Check if an flock file can be used or created. + + See the classes documentation about details. + + Args: + filename: The filename (without path) used for the flock file. + disabled: Disable the whole context managers behavior. This is a + workaround. See #1751 and :func:``Snapshots.backup()`` for + details. + + Raises: + RuntimeError: If it wasn't possible to use + """ + self._file_path = None + """Full path used for the flock file""" + + self._flock_handle = None + """File handle (descriptor) to the flock file.""" + + # Workaround for #1751. Remove after refactoring Snapshots.backup() + if disable: + return None + + folder = Path(Path.cwd().root) / 'run' / 'lock' + + if not folder.exists(): + # On older systems + folder = Path(Path.cwd().root) / 'var' / 'lock' + + self._file_path = folder / filename + + if self._can_use_file(self._file_path): + return None + + # Try user specific file lock + # e.g. /run/user/ + self._file_path = Path( + os.environ.get('XDG_RUNTIME_DIR', + f'/run/user/{os.getuid()}') + ) / filename + + if self._can_use_file(self._file_path): + return None + + # At last, try users cache dir. + self._file_path = Path( + os.environ.get('XDG_CACHE_HOME', + Path.home() / '.cache') + ) / filename + + if self._can_use_file(self._file_path): + return None + + raise RuntimeError( + f'Can not establish global flock file {self._file_path}') + + def _can_use_file(self, file_path: Path) -> bool: + """Check if ``file_path`` is usable as an flock file. + + The answer is ``True`` if the file exists without checking its + permissions. If not the file will be created and if successful + ``True`` will be returned. + + Returns: + bool: The answer. + + Raises: + PermissionError: Not enough permissions to create the file. + Exception: Any other error. + """ + if file_path.exists(): + return True + + # Try to create it + try: + file_path.touch(mode=0o666) + + except PermissionError: + logger.debug(f'Cannot use file lock on {file_path}.') + + except Exception as err: + logger.error( + f'Unknown error while testing file lock on {file_path}. ' + f'Please open a bug report. Error was {err}.') + + else: + logger.debug(f'Use {file_path} for file lock.') + return True + + return False + + def __enter__(self): + """Request an exclucive file lock on :data:``self._file_path``. + """ + # Workaround for #1751. Remove after refactoring Snapshots.backup() + # See __init__() for details + if self._file_path is None: + return None + + self._log('Set') + + # Open file for reading + self._flock_handle = self._file_path.open(mode='r') + + # blocks (waits) until an existing flock is released + fcntl.flock(self._flock_handle, fcntl.LOCK_EX) + + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + # Workaround for #1751. Remove after refactoring Snapshots.backup() + # See __init__() for details + if self._flock_handle is None: + return None + + self._log('Release') + fcntl.fcntl(self._flock_handle, fcntl.LOCK_UN) + self._flock_handle.close() + + def _log(self, prefix: str): + """Generate a log message including the current lock files path and the + process ID. + + Args: + prefix: Used in front of the log message. + """ + logger.debug(f'{prefix} flock {self._file_path} by PID {os.getpid()}') + + +class GlobalFlock(_FlockContext): + """Context manager used for global file lock in Back In Time. + + If it is a multi-user or single-user flock depends on the several + aspects. See :class:`_FlockContext` for details. + """ + + def __init__(self, disable: bool = False): + """See :func:`_FlockContext.__init__()` for details. + """ + super().__init__('backintime.lock', disable=disable) diff --git a/common/gocryptfstools.py b/common/gocryptfstools.py new file mode 100644 index 000000000..357213ba4 --- /dev/null +++ b/common/gocryptfstools.py @@ -0,0 +1,173 @@ +# SPDX-FileCopyrightText: © 2017 Germar Reitze +# SPDX-FileCopyrightText: © 2025 David Wales (@daviewales) +# SPDX-FileCopyrightText: © 2025 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +import os +import subprocess + +from pathlib import Path +import logger +from password_ipc import TempPasswordThread +from mount import MountControl +from exceptions import MountException + + +class GocryptfsMount(MountControl): + """Mounts a local GoCryptFS encrypted directory. + + After mounting the dir is accessible in plaintext (encrypted). + """ + + def __init__(self, *args, **kwargs): + super(GocryptfsMount, self).__init__(*args, **kwargs) + + # Workaround for some linters. + self.path = None + self.reverse = None + self.config_path = None + + self.setattrKwargs( + 'path', self.config.localGocryptfsPath(self.profile_id), **kwargs + ) + self.setattrKwargs('reverse', False, **kwargs) + self.setattrKwargs('password', None, store=False, **kwargs) + self.setattrKwargs('config_path', None, **kwargs) + + self.setDefaultArgs() + + self.mountproc = 'gocryptfs' + self.log_command = f'{self.mode}: {self.path}' + self.symlink_subfolder = None + + def _mount(self): + """ + mount the service + """ + if self.password is None: + self.password = self.config.password( + self.parent, self.profile_id, self.mode + ) + thread = TempPasswordThread(self.password) + env = os.environ.copy() + env['ASKPASS_TEMP'] = thread.temp_file + + with thread.starter(): + gocryptfs = [ + self.mountproc, + '-extpass', + 'backintime-askpass', + '-quiet' + ] + if self.reverse: + gocryptfs += ['-reverse'] + + gocryptfs += [ + self.path, + self.currentMountpoint + ] + + logger.debug(f'Call mount command: {gocryptfs}', self) + + proc = subprocess.Popen( + gocryptfs, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True + ) + output = proc.communicate()[0] + + if proc.returncode: + msg = _('Unable to mount "{command}"').format( + command=' '.join(gocryptfs) + ) + raise MountException( + f'{msg}:\n\n{output}\n\nReturn code: {proc.returncode}' + ) + + def init_backend(self): + """init the cipher path""" + + self.checkFuse() # gocryptfs binary available? + + if self.password is None: + self.password = self.config.password( + self.parent, self.profile_id, self.mode) + + # Dev note: See docstring in EncFS_mount._mount() for detailed + # description about the password thing. + thread = TempPasswordThread(self.password) + env = os.environ.copy() + env['ASKPASS_TEMP'] = thread.temp_file + + with thread.starter(): + # if not os.path.isdir(self.path): + # os.makedirs(self.path, exist_ok=True) + + gocryptfs = [ + self.mountproc, + '-extpass', + 'backintime-askpass'] + + gocryptfs.append('-init') + + gocryptfs.append(self.path) + + logger.debug( + f'Call command to create gocryptfs config file: {gocryptfs}', + self + ) + + proc = subprocess.Popen( + gocryptfs, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + + output = proc.communicate()[0] + + if proc.returncode: + msg = _('Unable to init encrypted path "{command}"').format( + command=' '.join(gocryptfs) + ) + msg = f'{msg}:\n\n{output}\n\nReturn code: {proc.returncode}' + logger.critical(msg, self) + raise MountException(msg) + + def preMountCheck(self, first_run=False): + """ + check what ever conditions must be given for the mount + """ + self.checkFuse() + + if first_run: + pass + + return True + + def configFile(self) -> str: + """Full path of gocryptfs config file""" + fn = 'gocryptfs.conf' + + if self.config_path is None: + return os.path.join(self.path, fn) + + return os.path.join(self.config_path, fn) + + def isConfigured(self) -> bool: + """Check if `gocryptfs.conf` exists.""" + fp_conf = Path(self.configFile()) + + if fp_conf.exists(): + logger.debug(f'Found gocryptfs config file in {fp_conf}', self) + return True + + logger.debug(f'No gocryptfs config found. Missing {fp_conf}', self) + + return False diff --git a/common/guiapplicationinstance.py b/common/guiapplicationinstance.py index 1a3046ad5..e404461c3 100644 --- a/common/guiapplicationinstance.py +++ b/common/guiapplicationinstance.py @@ -1,61 +1,54 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os - import logger from applicationinstance import ApplicationInstance + class GUIApplicationInstance(ApplicationInstance): + """Handle one application instance mechanism. """ - class used to handle one application instance mechanism - """ - def __init__(self, baseControlFile, raiseCmd = ''): - """ - specify the base for control files - """ + def __init__(self, baseControlFile, raiseCmd=''): + """Specify the base for control files.""" self.raiseFile = baseControlFile + '.raise' self.raiseCmd = raiseCmd - super(GUIApplicationInstance, self).__init__(baseControlFile + '.pid', False, False) + super(GUIApplicationInstance, self).__init__( + baseControlFile + '.pid', False, False) - #remove raiseFile is already exists + # Remove raiseFile is already exists if os.path.exists(self.raiseFile): os.remove(self.raiseFile) - self.check(raiseCmd) + self.check() self.startApplication() - def check(self, raiseCmd): - """ - check if the current application is already running - """ + def check(self): + """Check if the current application is already running.""" ret = super(GUIApplicationInstance, self).check(False) if not ret: - print("The application is already running! (pid: %s)" % self.pid) - #notify raise + print(f'The application is already running. (pid: {self.pid})') + + # Notify raise try: with open(self.raiseFile, 'wt') as f: - f.write(raiseCmd) + f.write(self.raiseCmd) + except OSError as e: - logger.error('Failed to write raise file %s: [%s] %s' %(e.filename, e.errno, e.strerror)) + logger.error(f'Failed to write raise file {e.filename}: ' + f'[{e.errno}] {e.strerror}') + + # Exit raise an exception so don't put it in a try/except block + exit(0) - exit(0) #exit raise an exception so don't put it in a try/except block else: return ret diff --git a/common/inhibitsuspend.py b/common/inhibitsuspend.py new file mode 100644 index 000000000..253f7ee4b --- /dev/null +++ b/common/inhibitsuspend.py @@ -0,0 +1,214 @@ +# SPDX-FileCopyrightText: © 2014 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Split from common/tools.py +"""Manage inhibition of suspend mode via DBus. +""" +import os +import sys +import dbus +import logger +import bitbase + + +class InhibitSuspend: + """Context manager to prevent machine to go to suspend or hibernate.""" + + def __init__(self, reason: str = None, app_id: str = None): + self.app_id = app_id if app_id else sys.argv[0] + self.reason = reason if reason else 'take snapshot' + self.cookie = None + self.bus = None + self.interface = None + self.file_descs = [] + + # Order is important. Don't change it for no good reason. + self.providers = { + 'freedesktop.login1': + self._inhibit_via_freedesktop_login_one, + 'freedesktop.PowerManagment': + self._inhibit_via_freedesktop_power_management, + 'gnome': + self._inhibit_via_gnome, + 'mate': + self._inhibit_via_mate, + } + + def _open_system_bus(self): + try: + self.bus = dbus.SystemBus() + except dbus.exceptions.DBusException as exc: + logger.error(f'Unable to open DBus system bus. {exc}') + + def _open_session_bus(self): + # Fixes #1592 (BiT hangs as root when trying to establish a dbus user + # session connection) + # Side effect: In BiT <= 1.4.1 root still tried to connect to the dbus + # user session and it may have worked sometimes (without logging we + # don't know) so as root suspend can no longer inhibited. + if bitbase.IS_IN_ROOT_MODE: + # Dev note (buhtz, 2025-04): But does this need to be a "Fail"? + logger.debug( + 'Inhibit Suspend aborted because BIT was started as root.') + return + + # Connect directly to the socket instead of dbus.SessionBus because + # the dbus.SessionBus was initiated before we loaded the environ + # variables and might not work. + try: + self.bus = dbus.bus.BusConnection( + os.environ['DBUS_SESSION_BUS_ADDRESS']) + + except KeyError: + pass + + except dbus.exceptions.DBusException as exc: + logger.error(f'Unable to open DBus session bus. {exc}') + + if self.bus: + return + + try: + # This code may hang forever (if BIT is run as root via cron + # job and no user is logged in). See #1592 + self.bus = dbus.SessionBus() + + except dbus.exceptions.DBusException as exc: + logger.error(f'Unable to open DBus session bus. {exc}') + + def _inhibit_via_freedesktop_login_one(self): + """Inhibit using system bus and login1 method. + + Method should be available on modern systems with and without + systemd. Uninhibition is done via closing a file descriptor. + """ + self._open_system_bus() + + if not self.bus: + return False + + try: + obj = self.bus.get_object( + bus_name='org.freedesktop.login1', + object_path='/org/freedesktop/login1') + + iface = dbus.Interface( + object=obj, + dbus_interface='org.freedesktop.login1.Manager') + + # Inhibition is active until this file descriptor is closed + file_desc = iface.Inhibit( + 'sleep', self.app_id, self.reason, "block").take() + + except dbus.DBusException as exc: + logger.debug(f'Inhibition (via "login1") failed: {exc}') + return False + + self.file_descs.append(file_desc) + + return True + + def _inhibit_generic_in_session(self, + bus_name: str, + object_path: str, + dbus_interface: str, + args: tuple) -> bool: + if not self.bus: + return False + + try: + obj = self.bus.get_object( + bus_name=bus_name, object_path=object_path) + + self.interface = dbus.Interface( + object=obj, dbus_interface=dbus_interface) + + self.cookie = self.interface.Inhibit(*args) + + except dbus.DBusException as exc: + logger.debug(f'Inhibition (via "{bus_name}") failed: {exc}') + return False + + return True + + def _inhibit_via_freedesktop_power_management(self): + return self._inhibit_generic_in_session( + bus_name='org.freedesktop.PowerManagement', + object_path='/org/freedesktop/Inhibit', + dbus_interface='org.freedesktop.PowerManager.Inhibit', + args=( + self.app_id, + self.reason + ) + ) + + def _inhibit_via_gnome(self): + return self._inhibit_generic_in_session( + bus_name='org.gnome.SessionManager', + object_path='/org/gnome/SessionManager', + dbus_interface='org.gnome.SessionManager.Inhibit', + args=( + self.app_id, + 0, # xwindow-id not relevant today + self.reason, + dbus.UInt32(4), # 4 = SUSPEND + ) + ) + + def _inhibit_via_mate(self): + return self._inhibit_generic_in_session( + bus_name='org.mate.SessionManager', + object_path='/org/mate/SessionManager', + dbus_interface='org.mate.SessionManager.Inhibit', + args=( + self.app_id, + 0, # xwindow-id not relevant today + self.reason, + dbus.UInt32(4), # 4 = SUSPEND + ) + ) + + def __enter__(self): + failed = [] + for name, inhibit in self.providers.items(): + # logger.debug(f'Try inhibiting suspend mode via "{name}"') + + result = inhibit() + + if result: + logger.info(f'Suspend mode inhibited via "{name}"') + break + + failed.append(name) + + result = True + if failed: + if result: + logger.debug('Inhibiting suspend mode failed ' + f'beforehand with {failed}') + else: + logger.error('Inhibiting suspend mode failed. ' + f'Tried with {failed}') + + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + try: + if self.cookie: + self.interface.UnInhibit(self.cookie) + + for fd in self.file_descs: + os.close(fd) + + except dbus.exceptions.DBusException as exc: + logger.error(f'Release suspend mode inhibition failed: {exc}') + + else: + if self.cookie or self.file_descs: + logger.info('Released suspend mode inhibition') diff --git a/common/languages.py b/common/languages.py new file mode 100644 index 000000000..d9cec0c47 --- /dev/null +++ b/common/languages.py @@ -0,0 +1,2224 @@ +# SPDX-FileCopyrightText: © 2023 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Generated at Sun Mar 8 12:51:09 2026 with help +# of package "babel" and "polib". +# https://babel.pocoo.org +# https://github.com/python-babel/babel +# pylint: disable=too-many-lines,missing-module-docstring +names = { + 'ar': { + '_native': 'العربية', + 'zh_CN': '阿拉伯语', + 'is': 'arabíska', + 'et': 'araabia', + 'sr': 'арапски', + 'nl': 'Arabisch', + 'sl': 'arabščina', + 'fa': 'عربی', + 'zh_TW': '阿拉伯文', + 'bg': 'арабски', + 'gl': 'árabe', + 'lt': 'arabų', + 'eu': 'arabiera', + 'he': 'ערבית', + 'pl': 'arabski', + 'sk': 'arabčina', + 'cs': 'arabština', + 'ka': 'არაბული', + 'de': 'Arabisch', + 'hu': 'arab', + 'ca': 'àrab', + 'ar': 'العربية', + 'ro': 'arabă', + 'fo': 'arabiskt', + 'tr': 'Arapça', + 'nb': 'arabisk', + 'pt': 'árabe', + 'it': 'arabo', + 'uk': 'арабська', + 'fi': 'arabia', + 'ko': '아랍어', + 'hr': 'arapski', + 'vi': 'Tiếng Ả Rập', + 'pt_BR': 'árabe', + 'el': 'Αραβικά', + 'ru': 'арабский', + 'eo': 'araba', + 'ja': 'アラビア語', + 'sr_Latn': 'arapski', + 'sv': 'arabiska', + 'da': 'arabisk', + 'id': 'Arab', + 'fr': 'arabe', + 'es': 'árabe', + 'nn': 'arabisk', + 'en': 'Arabic', + }, + 'bg': { + '_native': 'български', + 'zh_CN': '保加利亚语', + 'is': 'búlgarska', + 'et': 'bulgaaria', + 'sr': 'бугарски', + 'nl': 'Bulgaars', + 'sl': 'bolgarščina', + 'fa': 'بلغاری', + 'zh_TW': '保加利亞文', + 'bg': 'български', + 'gl': 'búlgaro', + 'lt': 'bulgarų', + 'eu': 'bulgariera', + 'he': 'בולגרית', + 'pl': 'bułgarski', + 'sk': 'bulharčina', + 'cs': 'bulharština', + 'ka': 'ბულგარული', + 'de': 'Bulgarisch', + 'hu': 'bolgár', + 'ca': 'búlgar', + 'ar': 'البلغارية', + 'ro': 'bulgară', + 'fo': 'bulgarskt', + 'tr': 'Bulgarca', + 'nb': 'bulgarsk', + 'pt': 'búlgaro', + 'it': 'bulgaro', + 'uk': 'болгарська', + 'fi': 'bulgaria', + 'ko': '불가리아어', + 'hr': 'bugarski', + 'vi': 'Tiếng Bulgaria', + 'pt_BR': 'búlgaro', + 'el': 'Βουλγαρικά', + 'ru': 'болгарский', + 'eo': 'bulgara', + 'ja': 'ブルガリア語', + 'sr_Latn': 'bugarski', + 'sv': 'bulgariska', + 'da': 'bulgarsk', + 'id': 'Bulgaria', + 'fr': 'bulgare', + 'es': 'búlgaro', + 'nn': 'bulgarsk', + 'en': 'Bulgarian', + }, + 'ca': { + '_native': 'català', + 'zh_CN': '加泰罗尼亚语', + 'is': 'katalónska', + 'et': 'katalaani', + 'sr': 'каталонски', + 'nl': 'Catalaans', + 'sl': 'katalonščina', + 'fa': 'کاتالان', + 'zh_TW': '加泰蘭文', + 'bg': 'каталонски', + 'gl': 'catalán', + 'lt': 'katalonų', + 'eu': 'katalana', + 'he': 'קטלאנית', + 'pl': 'kataloński', + 'sk': 'katalánčina', + 'cs': 'katalánština', + 'ka': 'კატალანური', + 'de': 'Katalanisch', + 'hu': 'katalán', + 'ca': 'català', + 'ar': 'الكتالانية', + 'ro': 'catalană', + 'fo': 'katalani', + 'tr': 'Katalanca', + 'nb': 'katalansk', + 'pt': 'catalão', + 'it': 'catalano', + 'uk': 'каталонська', + 'fi': 'katalaani', + 'ko': '카탈로니아어', + 'hr': 'katalonski', + 'vi': 'Tiếng Catalan', + 'pt_BR': 'catalão', + 'el': 'Καταλανικά', + 'ru': 'каталанский', + 'eo': 'kataluna', + 'ja': 'カタロニア語', + 'sr_Latn': 'katalonski', + 'sv': 'katalanska', + 'da': 'catalansk', + 'id': 'Katalan', + 'fr': 'catalan', + 'es': 'catalán', + 'nn': 'katalansk', + 'en': 'Catalan', + }, + 'cs': { + '_native': 'čeština', + 'zh_CN': '捷克语', + 'is': 'tékkneska', + 'et': 'tšehhi', + 'sr': 'чешки', + 'nl': 'Tsjechisch', + 'sl': 'češčina', + 'fa': 'چکی', + 'zh_TW': '捷克文', + 'bg': 'чешки', + 'gl': 'checo', + 'lt': 'čekų', + 'eu': 'txekiera', + 'he': 'צ׳כית', + 'pl': 'czeski', + 'sk': 'čeština', + 'cs': 'čeština', + 'ka': 'ჩეხური', + 'de': 'Tschechisch', + 'hu': 'cseh', + 'ca': 'txec', + 'ar': 'التشيكية', + 'ro': 'cehă', + 'fo': 'kekkiskt', + 'tr': 'Çekçe', + 'nb': 'tsjekkisk', + 'pt': 'tcheco', + 'it': 'ceco', + 'uk': 'чеська', + 'fi': 'tšekki', + 'ko': '체코어', + 'hr': 'češki', + 'vi': 'Tiếng Séc', + 'pt_BR': 'tcheco', + 'el': 'Τσεχικά', + 'ru': 'чешский', + 'eo': 'ĉeĥa', + 'ja': 'チェコ語', + 'sr_Latn': 'češki', + 'sv': 'tjeckiska', + 'da': 'tjekkisk', + 'id': 'Ceko', + 'fr': 'tchèque', + 'es': 'checo', + 'nn': 'tsjekkisk', + 'en': 'Czech', + }, + 'da': { + '_native': 'dansk', + 'zh_CN': '丹麦语', + 'is': 'danska', + 'et': 'taani', + 'sr': 'дански', + 'nl': 'Deens', + 'sl': 'danščina', + 'fa': 'دانمارکی', + 'zh_TW': '丹麥文', + 'bg': 'датски', + 'gl': 'dinamarqués', + 'lt': 'danų', + 'eu': 'daniera', + 'he': 'דנית', + 'pl': 'duński', + 'sk': 'dánčina', + 'cs': 'dánština', + 'ka': 'დანიური', + 'de': 'Dänisch', + 'hu': 'dán', + 'ca': 'danès', + 'ar': 'الدانمركية', + 'ro': 'daneză', + 'fo': 'danskt', + 'tr': 'Danca', + 'nb': 'dansk', + 'pt': 'dinamarquês', + 'it': 'danese', + 'uk': 'данська', + 'fi': 'tanska', + 'ko': '덴마크어', + 'hr': 'danski', + 'vi': 'Tiếng Đan Mạch', + 'pt_BR': 'dinamarquês', + 'el': 'Δανικά', + 'ru': 'датский', + 'eo': 'dana', + 'ja': 'デンマーク語', + 'sr_Latn': 'danski', + 'sv': 'danska', + 'da': 'dansk', + 'id': 'Dansk', + 'fr': 'danois', + 'es': 'danés', + 'nn': 'dansk', + 'en': 'Danish', + }, + 'de': { + '_native': 'Deutsch', + 'zh_CN': '德语', + 'is': 'þýska', + 'et': 'saksa', + 'sr': 'немачки', + 'nl': 'Duits', + 'sl': 'nemščina', + 'fa': 'آلمانی', + 'zh_TW': '德文', + 'bg': 'немски', + 'gl': 'alemán', + 'lt': 'vokiečių', + 'eu': 'alemana', + 'he': 'גרמנית', + 'pl': 'niemiecki', + 'sk': 'nemčina', + 'cs': 'němčina', + 'ka': 'გერმანული', + 'de': 'Deutsch', + 'hu': 'német', + 'ca': 'alemany', + 'ar': 'الألمانية', + 'ro': 'germană', + 'fo': 'týskt', + 'tr': 'Almanca', + 'nb': 'tysk', + 'pt': 'alemão', + 'it': 'tedesco', + 'uk': 'німецька', + 'fi': 'saksa', + 'ko': '독일어', + 'hr': 'njemački', + 'vi': 'Tiếng Đức', + 'pt_BR': 'alemão', + 'el': 'Γερμανικά', + 'ru': 'немецкий', + 'eo': 'germana', + 'ja': 'ドイツ語', + 'sr_Latn': 'nemački', + 'sv': 'tyska', + 'da': 'tysk', + 'id': 'Jerman', + 'fr': 'allemand', + 'es': 'alemán', + 'nn': 'tysk', + 'en': 'German', + }, + 'el': { + '_native': 'Ελληνικά', + 'zh_CN': '希腊语', + 'is': 'gríska', + 'et': 'kreeka', + 'sr': 'грчки', + 'nl': 'Grieks', + 'sl': 'grščina', + 'fa': 'یونانی', + 'zh_TW': '希臘文', + 'bg': 'гръцки', + 'gl': 'grego', + 'lt': 'graikų', + 'eu': 'greziera', + 'he': 'יוונית', + 'pl': 'grecki', + 'sk': 'gréčtina', + 'cs': 'řečtina', + 'ka': 'ბერძნული', + 'de': 'Griechisch', + 'hu': 'görög', + 'ca': 'grec', + 'ar': 'اليونانية', + 'ro': 'greacă', + 'fo': 'grikskt', + 'tr': 'Yunanca', + 'nb': 'gresk', + 'pt': 'grego', + 'it': 'greco', + 'uk': 'грецька', + 'fi': 'kreikka', + 'ko': '그리스어', + 'hr': 'grčki', + 'vi': 'Tiếng Hy Lạp', + 'pt_BR': 'grego', + 'el': 'Ελληνικά', + 'ru': 'греческий', + 'eo': 'greka', + 'ja': 'ギリシャ語', + 'sr_Latn': 'grčki', + 'sv': 'grekiska', + 'da': 'græsk', + 'id': 'Yunani', + 'fr': 'grec', + 'es': 'griego', + 'nn': 'gresk', + 'en': 'Greek', + }, + 'en': { + '_native': 'English', + 'zh_CN': '英语', + 'is': 'enska', + 'et': 'inglise', + 'sr': 'енглески', + 'nl': 'Engels', + 'sl': 'angleščina', + 'fa': 'انگلیسی', + 'zh_TW': '英文', + 'bg': 'английски', + 'gl': 'inglés', + 'lt': 'anglų', + 'eu': 'ingelesa', + 'he': 'אנגלית', + 'pl': 'angielski', + 'sk': 'angličtina', + 'cs': 'angličtina', + 'ka': 'ინგლისური', + 'de': 'Englisch', + 'hu': 'angol', + 'ca': 'anglès', + 'ar': 'الإنجليزية', + 'ro': 'engleză', + 'fo': 'enskt', + 'tr': 'İngilizce', + 'nb': 'engelsk', + 'pt': 'inglês', + 'it': 'inglese', + 'uk': 'англійська', + 'fi': 'englanti', + 'ko': '영어', + 'hr': 'engleski', + 'vi': 'Tiếng Anh', + 'pt_BR': 'inglês', + 'el': 'Αγγλικά', + 'ru': 'английский', + 'eo': 'angla', + 'ja': '英語', + 'sr_Latn': 'engleski', + 'sv': 'engelska', + 'da': 'engelsk', + 'id': 'Inggris', + 'fr': 'anglais', + 'es': 'inglés', + 'nn': 'engelsk', + 'en': 'English', + }, + 'eo': { + '_native': 'Esperanto', + 'zh_CN': '世界语', + 'is': 'esperantó', + 'et': 'esperanto', + 'sr': 'есперанто', + 'nl': 'Esperanto', + 'sl': 'esperanto', + 'fa': 'اسپرانتو', + 'zh_TW': '世界文', + 'bg': 'есперанто', + 'gl': 'esperanto', + 'lt': 'esperanto', + 'eu': 'esperantoa', + 'he': 'אספרנטו', + 'pl': 'esperanto', + 'sk': 'esperanto', + 'cs': 'esperanto', + 'ka': 'ესპერანტო', + 'de': 'Esperanto', + 'hu': 'eszperantó', + 'ca': 'esperanto', + 'ar': 'الإسبرانتو', + 'ro': 'esperanto', + 'fo': 'esperanto', + 'tr': 'Esperanto', + 'nb': 'esperanto', + 'pt': 'esperanto', + 'it': 'esperanto', + 'uk': 'есперанто', + 'fi': 'esperanto', + 'ko': '에스페란토어', + 'hr': 'esperanto', + 'vi': 'Tiếng Quốc Tế Ngữ', + 'pt_BR': 'esperanto', + 'el': 'Εσπεράντο', + 'ru': 'эсперанто', + 'eo': 'Esperanto', + 'ja': 'エスペラント語', + 'sr_Latn': 'esperanto', + 'sv': 'esperanto', + 'da': 'esperanto', + 'id': 'Esperanto', + 'fr': 'espéranto', + 'es': 'esperanto', + 'nn': 'esperanto', + 'en': 'Esperanto', + }, + 'es': { + '_native': 'español', + 'zh_CN': '西班牙语', + 'is': 'spænska', + 'et': 'hispaania', + 'sr': 'шпански', + 'nl': 'Spaans', + 'sl': 'španščina', + 'fa': 'اسپانیایی', + 'zh_TW': '西班牙文', + 'bg': 'испански', + 'gl': 'español', + 'lt': 'ispanų', + 'eu': 'gaztelania', + 'he': 'ספרדית', + 'pl': 'hiszpański', + 'sk': 'španielčina', + 'cs': 'španělština', + 'ka': 'ესპანური', + 'de': 'Spanisch', + 'hu': 'spanyol', + 'ca': 'espanyol', + 'ar': 'الإسبانية', + 'ro': 'spaniolă', + 'fo': 'spanskt', + 'tr': 'İspanyolca', + 'nb': 'spansk', + 'pt': 'espanhol', + 'it': 'spagnolo', + 'uk': 'іспанська', + 'fi': 'espanja', + 'ko': '스페인어', + 'hr': 'španjolski', + 'vi': 'Tiếng Tây Ban Nha', + 'pt_BR': 'espanhol', + 'el': 'Ισπανικά', + 'ru': 'испанский', + 'eo': 'hispana', + 'ja': 'スペイン語', + 'sr_Latn': 'španski', + 'sv': 'spanska', + 'da': 'spansk', + 'id': 'Spanyol', + 'fr': 'espagnol', + 'es': 'español', + 'nn': 'spansk', + 'en': 'Spanish', + }, + 'et': { + '_native': 'eesti', + 'zh_CN': '爱沙尼亚语', + 'is': 'eistneska', + 'et': 'eesti', + 'sr': 'естонски', + 'nl': 'Estisch', + 'sl': 'estonščina', + 'fa': 'استونیایی', + 'zh_TW': '愛沙尼亞文', + 'bg': 'естонски', + 'gl': 'estoniano', + 'lt': 'estų', + 'eu': 'estoniera', + 'he': 'אסטונית', + 'pl': 'estoński', + 'sk': 'estónčina', + 'cs': 'estonština', + 'ka': 'ესტონური', + 'de': 'Estnisch', + 'hu': 'észt', + 'ca': 'estonià', + 'ar': 'الإستونية', + 'ro': 'estonă', + 'fo': 'estiskt', + 'tr': 'Estonca', + 'nb': 'estisk', + 'pt': 'estoniano', + 'it': 'estone', + 'uk': 'естонська', + 'fi': 'viro', + 'ko': '에스토니아어', + 'hr': 'estonski', + 'vi': 'Tiếng Estonia', + 'pt_BR': 'estoniano', + 'el': 'Εσθονικά', + 'ru': 'эстонский', + 'eo': 'estona', + 'ja': 'エストニア語', + 'sr_Latn': 'estonski', + 'sv': 'estniska', + 'da': 'estisk', + 'id': 'Estonia', + 'fr': 'estonien', + 'es': 'estonio', + 'nn': 'estisk', + 'en': 'Estonian', + }, + 'eu': { + '_native': 'euskara', + 'zh_CN': '巴斯克语', + 'is': 'baskneska', + 'et': 'baski', + 'sr': 'баскијски', + 'nl': 'Baskisch', + 'sl': 'baskovščina', + 'fa': 'باسکی', + 'zh_TW': '巴斯克文', + 'bg': 'баски', + 'gl': 'éuscaro', + 'lt': 'baskų', + 'eu': 'euskara', + 'he': 'בסקית', + 'pl': 'baskijski', + 'sk': 'baskičtina', + 'cs': 'baskičtina', + 'ka': 'ბასკური', + 'de': 'Baskisch', + 'hu': 'baszk', + 'ca': 'basc', + 'ar': 'الباسكية', + 'ro': 'bască', + 'fo': 'baskiskt', + 'tr': 'Baskça', + 'nb': 'baskisk', + 'pt': 'basco', + 'it': 'basco', + 'uk': 'баскська', + 'fi': 'baski', + 'ko': '바스크어', + 'hr': 'baskijski', + 'vi': 'Tiếng Basque', + 'pt_BR': 'basco', + 'el': 'Βασκικά', + 'ru': 'баскский', + 'eo': 'eŭska', + 'ja': 'バスク語', + 'sr_Latn': 'baskijski', + 'sv': 'baskiska', + 'da': 'baskisk', + 'id': 'Basque', + 'fr': 'basque', + 'es': 'euskera', + 'nn': 'baskisk', + 'en': 'Basque', + }, + 'fa': { + '_native': 'فارسی', + 'zh_CN': '波斯语', + 'is': 'persneska', + 'et': 'pärsia', + 'sr': 'персијски', + 'nl': 'Perzisch', + 'sl': 'perzijščina', + 'fa': 'فارسی', + 'zh_TW': '波斯文', + 'bg': 'персийски', + 'gl': 'persa', + 'lt': 'persų', + 'eu': 'persiera', + 'he': 'פרסית', + 'pl': 'perski', + 'sk': 'perzština', + 'cs': 'perština', + 'ka': 'სპარსული', + 'de': 'Persisch', + 'hu': 'perzsa', + 'ca': 'persa', + 'ar': 'الفارسية', + 'ro': 'persană', + 'fo': 'persiskt', + 'tr': 'Farsça', + 'nb': 'persisk', + 'pt': 'persa', + 'it': 'persiano', + 'uk': 'перська', + 'fi': 'persia', + 'ko': '페르시아어', + 'hr': 'perzijski', + 'vi': 'Tiếng Ba Tư', + 'pt_BR': 'persa', + 'el': 'Περσικά', + 'ru': 'персидский', + 'eo': 'persa', + 'ja': 'ペルシア語', + 'sr_Latn': 'persijski', + 'sv': 'persiska', + 'da': 'persisk', + 'id': 'Persia', + 'fr': 'persan', + 'es': 'persa', + 'nn': 'persisk', + 'en': 'Persian', + }, + 'fi': { + '_native': 'suomi', + 'zh_CN': '芬兰语', + 'is': 'finnska', + 'et': 'soome', + 'sr': 'фински', + 'nl': 'Fins', + 'sl': 'finščina', + 'fa': 'فنلاندی', + 'zh_TW': '芬蘭文', + 'bg': 'фински', + 'gl': 'finés', + 'lt': 'suomių', + 'eu': 'finlandiera', + 'he': 'פינית', + 'pl': 'fiński', + 'sk': 'fínčina', + 'cs': 'finština', + 'ka': 'ფინური', + 'de': 'Finnisch', + 'hu': 'finn', + 'ca': 'finès', + 'ar': 'الفنلندية', + 'ro': 'finlandeză', + 'fo': 'finskt', + 'tr': 'Fince', + 'nb': 'finsk', + 'pt': 'finlandês', + 'it': 'finlandese', + 'uk': 'фінська', + 'fi': 'suomi', + 'ko': '핀란드어', + 'hr': 'finski', + 'vi': 'Tiếng Phần Lan', + 'pt_BR': 'finlandês', + 'el': 'Φινλανδικά', + 'ru': 'финский', + 'eo': 'finna', + 'ja': 'フィンランド語', + 'sr_Latn': 'finski', + 'sv': 'finska', + 'da': 'finsk', + 'id': 'Suomi', + 'fr': 'finnois', + 'es': 'finés', + 'nn': 'finsk', + 'en': 'Finnish', + }, + 'fo': { + '_native': 'føroyskt', + 'zh_CN': '法罗语', + 'is': 'færeyska', + 'et': 'fääri', + 'sr': 'фарски', + 'nl': 'Faeröers', + 'sl': 'ferščina', + 'fa': 'فارویی', + 'zh_TW': '法羅文', + 'bg': 'фарьорски', + 'gl': 'feroés', + 'lt': 'farerų', + 'eu': 'faroera', + 'he': 'פארואזית', + 'pl': 'farerski', + 'sk': 'faerčina', + 'cs': 'faerština', + 'ka': 'ფარერული', + 'de': 'Färöisch', + 'hu': 'feröeri', + 'ca': 'feroès', + 'ar': 'الفاروية', + 'ro': 'feroeză', + 'fo': 'føroyskt', + 'tr': 'Faroe dili', + 'nb': 'færøysk', + 'pt': 'feroês', + 'it': 'faroese', + 'uk': 'фарерська', + 'fi': 'fääri', + 'ko': '페로어', + 'hr': 'ferojski', + 'vi': 'Tiếng Faroe', + 'pt_BR': 'feroês', + 'el': 'Φεροϊκά', + 'ru': 'фарерский', + 'eo': 'feroa', + 'ja': 'フェロー語', + 'sr_Latn': 'farski', + 'sv': 'färöiska', + 'da': 'færøsk', + 'id': 'Faroe', + 'fr': 'féroïen', + 'es': 'feroés', + 'nn': 'færøysk', + 'en': 'Faroese', + }, + 'fr': { + '_native': 'français', + 'zh_CN': '法语', + 'is': 'franska', + 'et': 'prantsuse', + 'sr': 'француски', + 'nl': 'Frans', + 'sl': 'francoščina', + 'fa': 'فرانسوی', + 'zh_TW': '法文', + 'bg': 'френски', + 'gl': 'francés', + 'lt': 'prancūzų', + 'eu': 'frantsesa', + 'he': 'צרפתית', + 'pl': 'francuski', + 'sk': 'francúzština', + 'cs': 'francouzština', + 'ka': 'ფრანგული', + 'de': 'Französisch', + 'hu': 'francia', + 'ca': 'francès', + 'ar': 'الفرنسية', + 'ro': 'franceză', + 'fo': 'franskt', + 'tr': 'Fransızca', + 'nb': 'fransk', + 'pt': 'francês', + 'it': 'francese', + 'uk': 'французька', + 'fi': 'ranska', + 'ko': '프랑스어', + 'hr': 'francuski', + 'vi': 'Tiếng Pháp', + 'pt_BR': 'francês', + 'el': 'Γαλλικά', + 'ru': 'французский', + 'eo': 'franca', + 'ja': 'フランス語', + 'sr_Latn': 'francuski', + 'sv': 'franska', + 'da': 'fransk', + 'id': 'Prancis', + 'fr': 'français', + 'es': 'francés', + 'nn': 'fransk', + 'en': 'French', + }, + 'gl': { + '_native': 'galego', + 'zh_CN': '加利西亚语', + 'is': 'galisíska', + 'et': 'galeegi', + 'sr': 'галицијски', + 'nl': 'Galicisch', + 'sl': 'galicijščina', + 'fa': 'گالیسیایی', + 'zh_TW': '加利西亞文', + 'bg': 'галисийски', + 'gl': 'galego', + 'lt': 'galisų', + 'eu': 'galiziera', + 'he': 'גליציאנית', + 'pl': 'galicyjski', + 'sk': 'galícijčina', + 'cs': 'galicijština', + 'ka': 'გალისიური', + 'de': 'Galicisch', + 'hu': 'gallego', + 'ca': 'gallec', + 'ar': 'الجاليكية', + 'ro': 'galiciană', + 'fo': 'galisiskt', + 'tr': 'Galiçyaca', + 'nb': 'galisisk', + 'pt': 'galego', + 'it': 'galiziano', + 'uk': 'галісійська', + 'fi': 'galicia', + 'ko': '갈리시아어', + 'hr': 'galicijski', + 'vi': 'Tiếng Galician', + 'pt_BR': 'galego', + 'el': 'Γαλικιανά', + 'ru': 'галисийский', + 'eo': 'galega', + 'ja': 'ガリシア語', + 'sr_Latn': 'galicijski', + 'sv': 'galiciska', + 'da': 'galicisk', + 'id': 'Galisia', + 'fr': 'galicien', + 'es': 'gallego', + 'nn': 'galisisk', + 'en': 'Galician', + }, + 'he': { + '_native': 'עברית', + 'zh_CN': '希伯来语', + 'is': 'hebreska', + 'et': 'heebrea', + 'sr': 'хебрејски', + 'nl': 'Hebreeuws', + 'sl': 'hebrejščina', + 'fa': 'عبری', + 'zh_TW': '希伯來文', + 'bg': 'иврит', + 'gl': 'hebreo', + 'lt': 'hebrajų', + 'eu': 'hebreera', + 'he': 'עברית', + 'pl': 'hebrajski', + 'sk': 'hebrejčina', + 'cs': 'hebrejština', + 'ka': 'ებრაული', + 'de': 'Hebräisch', + 'hu': 'héber', + 'ca': 'hebreu', + 'ar': 'العبرية', + 'ro': 'ebraică', + 'fo': 'hebraiskt', + 'tr': 'İbranice', + 'nb': 'hebraisk', + 'pt': 'hebraico', + 'it': 'ebraico', + 'uk': 'іврит', + 'fi': 'heprea', + 'ko': '히브리어', + 'hr': 'hebrejski', + 'vi': 'Tiếng Do Thái', + 'pt_BR': 'hebraico', + 'el': 'Εβραϊκά', + 'ru': 'иврит', + 'eo': 'hebrea', + 'ja': 'ヘブライ語', + 'sr_Latn': 'hebrejski', + 'sv': 'hebreiska', + 'da': 'hebraisk', + 'id': 'Ibrani', + 'fr': 'hébreu', + 'es': 'hebreo', + 'nn': 'hebraisk', + 'en': 'Hebrew', + }, + 'hr': { + '_native': 'hrvatski', + 'zh_CN': '克罗地亚语', + 'is': 'króatíska', + 'et': 'horvaadi', + 'sr': 'хрватски', + 'nl': 'Kroatisch', + 'sl': 'hrvaščina', + 'fa': 'کروات', + 'zh_TW': '克羅埃西亞文', + 'bg': 'хърватски', + 'gl': 'croata', + 'lt': 'kroatų', + 'eu': 'kroaziera', + 'he': 'קרואטית', + 'pl': 'chorwacki', + 'sk': 'chorvátčina', + 'cs': 'chorvatština', + 'ka': 'ხორვატული', + 'de': 'Kroatisch', + 'hu': 'horvát', + 'ca': 'croat', + 'ar': 'الكرواتية', + 'ro': 'croată', + 'fo': 'kroatiskt', + 'tr': 'Hırvatça', + 'nb': 'kroatisk', + 'pt': 'croata', + 'it': 'croato', + 'uk': 'хорватська', + 'fi': 'kroatia', + 'ko': '크로아티아어', + 'hr': 'hrvatski', + 'vi': 'Tiếng Croatia', + 'pt_BR': 'croata', + 'el': 'Κροατικά', + 'ru': 'хорватский', + 'eo': 'kroata', + 'ja': 'クロアチア語', + 'sr_Latn': 'hrvatski', + 'sv': 'kroatiska', + 'da': 'kroatisk', + 'id': 'Kroasia', + 'fr': 'croate', + 'es': 'croata', + 'nn': 'kroatisk', + 'en': 'Croatian', + }, + 'hu': { + '_native': 'magyar', + 'zh_CN': '匈牙利语', + 'is': 'ungverska', + 'et': 'ungari', + 'sr': 'мађарски', + 'nl': 'Hongaars', + 'sl': 'madžarščina', + 'fa': 'مجاری', + 'zh_TW': '匈牙利文', + 'bg': 'унгарски', + 'gl': 'húngaro', + 'lt': 'vengrų', + 'eu': 'hungariera', + 'he': 'הונגרית', + 'pl': 'węgierski', + 'sk': 'maďarčina', + 'cs': 'maďarština', + 'ka': 'უნგრული', + 'de': 'Ungarisch', + 'hu': 'magyar', + 'ca': 'hongarès', + 'ar': 'الهنغارية', + 'ro': 'maghiară', + 'fo': 'ungarskt', + 'tr': 'Macarca', + 'nb': 'ungarsk', + 'pt': 'húngaro', + 'it': 'ungherese', + 'uk': 'угорська', + 'fi': 'unkari', + 'ko': '헝가리어', + 'hr': 'mađarski', + 'vi': 'Tiếng Hungary', + 'pt_BR': 'húngaro', + 'el': 'Ουγγρικά', + 'ru': 'венгерский', + 'eo': 'hungara', + 'ja': 'ハンガリー語', + 'sr_Latn': 'mađarski', + 'sv': 'ungerska', + 'da': 'ungarsk', + 'id': 'Hungaria', + 'fr': 'hongrois', + 'es': 'húngaro', + 'nn': 'ungarsk', + 'en': 'Hungarian', + }, + 'id': { + '_native': 'Indonesia', + 'zh_CN': '印度尼西亚语', + 'is': 'indónesíska', + 'et': 'indoneesia', + 'sr': 'индонежански', + 'nl': 'Indonesisch', + 'sl': 'indonezijščina', + 'fa': 'اندونزیایی', + 'zh_TW': '印尼文', + 'bg': 'индонезийски', + 'gl': 'indonesio', + 'lt': 'indoneziečių', + 'eu': 'indonesiera', + 'he': 'אינדונזית', + 'pl': 'indonezyjski', + 'sk': 'indonézština', + 'cs': 'indonéština', + 'ka': 'ინდონეზიური', + 'de': 'Indonesisch', + 'hu': 'indonéz', + 'ca': 'indonesi', + 'ar': 'الإندونيسية', + 'ro': 'indoneziană', + 'fo': 'indonesiskt', + 'tr': 'Endonezce', + 'nb': 'indonesisk', + 'pt': 'indonésio', + 'it': 'indonesiano', + 'uk': 'індонезійська', + 'fi': 'indonesia', + 'ko': '인도네시아어', + 'hr': 'indonezijski', + 'vi': 'Tiếng Indonesia', + 'pt_BR': 'indonésio', + 'el': 'Ινδονησιακά', + 'ru': 'индонезийский', + 'eo': 'indonezia', + 'ja': 'インドネシア語', + 'sr_Latn': 'indonežanski', + 'sv': 'indonesiska', + 'da': 'indonesisk', + 'id': 'Indonesia', + 'fr': 'indonésien', + 'es': 'indonesio', + 'nn': 'indonesisk', + 'en': 'Indonesian', + }, + 'is': { + '_native': 'íslenska', + 'zh_CN': '冰岛语', + 'is': 'íslenska', + 'et': 'islandi', + 'sr': 'исландски', + 'nl': 'IJslands', + 'sl': 'islandščina', + 'fa': 'ایسلندی', + 'zh_TW': '冰島文', + 'bg': 'исландски', + 'gl': 'islandés', + 'lt': 'islandų', + 'eu': 'islandiera', + 'he': 'איסלנדית', + 'pl': 'islandzki', + 'sk': 'islandčina', + 'cs': 'islandština', + 'ka': 'ისლანდიური', + 'de': 'Isländisch', + 'hu': 'izlandi', + 'ca': 'islandès', + 'ar': 'الأيسلندية', + 'ro': 'islandeză', + 'fo': 'íslendskt', + 'tr': 'İzlandaca', + 'nb': 'islandsk', + 'pt': 'islandês', + 'it': 'islandese', + 'uk': 'ісландська', + 'fi': 'islanti', + 'ko': '아이슬란드어', + 'hr': 'islandski', + 'vi': 'Tiếng Iceland', + 'pt_BR': 'islandês', + 'el': 'Ισλανδικά', + 'ru': 'исландский', + 'eo': 'islanda', + 'ja': 'アイスランド語', + 'sr_Latn': 'islandski', + 'sv': 'isländska', + 'da': 'islandsk', + 'id': 'Islandia', + 'fr': 'islandais', + 'es': 'islandés', + 'nn': 'islandsk', + 'en': 'Icelandic', + }, + 'it': { + '_native': 'italiano', + 'zh_CN': '意大利语', + 'is': 'ítalska', + 'et': 'itaalia', + 'sr': 'италијански', + 'nl': 'Italiaans', + 'sl': 'italijanščina', + 'fa': 'ایتالیایی', + 'zh_TW': '義大利文', + 'bg': 'италиански', + 'gl': 'italiano', + 'lt': 'italų', + 'eu': 'italiera', + 'he': 'איטלקית', + 'pl': 'włoski', + 'sk': 'taliančina', + 'cs': 'italština', + 'ka': 'იტალიური', + 'de': 'Italienisch', + 'hu': 'olasz', + 'ca': 'italià', + 'ar': 'الإيطالية', + 'ro': 'italiană', + 'fo': 'italskt', + 'tr': 'İtalyanca', + 'nb': 'italiensk', + 'pt': 'italiano', + 'it': 'italiano', + 'uk': 'італійська', + 'fi': 'italia', + 'ko': '이탈리아어', + 'hr': 'talijanski', + 'vi': 'Tiếng Italy', + 'pt_BR': 'italiano', + 'el': 'Ιταλικά', + 'ru': 'итальянский', + 'eo': 'itala', + 'ja': 'イタリア語', + 'sr_Latn': 'italijanski', + 'sv': 'italienska', + 'da': 'italiensk', + 'id': 'Italia', + 'fr': 'italien', + 'es': 'italiano', + 'nn': 'italiensk', + 'en': 'Italian', + }, + 'ja': { + '_native': '日本語', + 'zh_CN': '日语', + 'is': 'japanska', + 'et': 'jaapani', + 'sr': 'јапански', + 'nl': 'Japans', + 'sl': 'japonščina', + 'fa': 'ژاپنی', + 'zh_TW': '日文', + 'bg': 'японски', + 'gl': 'xaponés', + 'lt': 'japonų', + 'eu': 'japoniera', + 'he': 'יפנית', + 'pl': 'japoński', + 'sk': 'japončina', + 'cs': 'japonština', + 'ka': 'იაპონური', + 'de': 'Japanisch', + 'hu': 'japán', + 'ca': 'japonès', + 'ar': 'اليابانية', + 'ro': 'japoneză', + 'fo': 'japanskt', + 'tr': 'Japonca', + 'nb': 'japansk', + 'pt': 'japonês', + 'it': 'giapponese', + 'uk': 'японська', + 'fi': 'japani', + 'ko': '일본어', + 'hr': 'japanski', + 'vi': 'Tiếng Nhật', + 'pt_BR': 'japonês', + 'el': 'Ιαπωνικά', + 'ru': 'японский', + 'eo': 'japana', + 'ja': '日本語', + 'sr_Latn': 'japanski', + 'sv': 'japanska', + 'da': 'japansk', + 'id': 'Jepang', + 'fr': 'japonais', + 'es': 'japonés', + 'nn': 'japansk', + 'en': 'Japanese', + }, + 'ka': { + '_native': 'ქართული', + 'zh_CN': '格鲁吉亚语', + 'is': 'georgíska', + 'et': 'gruusia', + 'sr': 'грузијски', + 'nl': 'Georgisch', + 'sl': 'gruzijščina', + 'fa': 'گرجی', + 'zh_TW': '喬治亞文', + 'bg': 'грузински', + 'gl': 'xeorxiano', + 'lt': 'gruzinų', + 'eu': 'georgiera', + 'he': 'גאורגית', + 'pl': 'gruziński', + 'sk': 'gruzínčina', + 'cs': 'gruzínština', + 'ka': 'ქართული', + 'de': 'Georgisch', + 'hu': 'grúz', + 'ca': 'georgià', + 'ar': 'الجورجية', + 'ro': 'georgiană', + 'fo': 'georgiskt', + 'tr': 'Gürcüce', + 'nb': 'georgisk', + 'pt': 'georgiano', + 'it': 'georgiano', + 'uk': 'грузинська', + 'fi': 'georgia', + 'ko': '조지아어', + 'hr': 'gruzijski', + 'vi': 'Tiếng Georgia', + 'pt_BR': 'georgiano', + 'el': 'Γεωργιανά', + 'ru': 'грузинский', + 'eo': 'kartvela', + 'ja': 'ジョージア語', + 'sr_Latn': 'gruzijski', + 'sv': 'georgiska', + 'da': 'georgisk', + 'id': 'Georgia', + 'fr': 'géorgien', + 'es': 'georgiano', + 'nn': 'georgisk', + 'en': 'Georgian', + }, + 'ko': { + '_native': '한국어', + 'zh_CN': '韩语', + 'is': 'kóreska', + 'et': 'korea', + 'sr': 'корејски', + 'nl': 'Koreaans', + 'sl': 'korejščina', + 'fa': 'کره‌ای', + 'zh_TW': '韓文', + 'bg': 'корейски', + 'gl': 'coreano', + 'lt': 'korėjiečių', + 'eu': 'koreera', + 'he': 'קוריאנית', + 'pl': 'koreański', + 'sk': 'kórejčina', + 'cs': 'korejština', + 'ka': 'კორეული', + 'de': 'Koreanisch', + 'hu': 'koreai', + 'ca': 'coreà', + 'ar': 'الكورية', + 'ro': 'coreeană', + 'fo': 'koreanskt', + 'tr': 'Korece', + 'nb': 'koreansk', + 'pt': 'coreano', + 'it': 'coreano', + 'uk': 'корейська', + 'fi': 'korea', + 'ko': '한국어', + 'hr': 'korejski', + 'vi': 'Tiếng Hàn', + 'pt_BR': 'coreano', + 'el': 'Κορεατικά', + 'ru': 'корейский', + 'eo': 'korea', + 'ja': '韓国語', + 'sr_Latn': 'korejski', + 'sv': 'koreanska', + 'da': 'koreansk', + 'id': 'Korea', + 'fr': 'coréen', + 'es': 'coreano', + 'nn': 'koreansk', + 'en': 'Korean', + }, + 'lt': { + '_native': 'lietuvių', + 'zh_CN': '立陶宛语', + 'is': 'litháíska', + 'et': 'leedu', + 'sr': 'литвански', + 'nl': 'Litouws', + 'sl': 'litovščina', + 'fa': 'لیتوانیایی', + 'zh_TW': '立陶宛文', + 'bg': 'литовски', + 'gl': 'lituano', + 'lt': 'lietuvių', + 'eu': 'lituaniera', + 'he': 'ליטאית', + 'pl': 'litewski', + 'sk': 'litovčina', + 'cs': 'litevština', + 'ka': 'ლიეტუვური', + 'de': 'Litauisch', + 'hu': 'litván', + 'ca': 'lituà', + 'ar': 'الليتوانية', + 'ro': 'lituaniană', + 'fo': 'litaviskt', + 'tr': 'Litvanca', + 'nb': 'litauisk', + 'pt': 'lituano', + 'it': 'lituano', + 'uk': 'литовська', + 'fi': 'liettua', + 'ko': '리투아니아어', + 'hr': 'litavski', + 'vi': 'Tiếng Litva', + 'pt_BR': 'lituano', + 'el': 'Λιθουανικά', + 'ru': 'литовский', + 'eo': 'litova', + 'ja': 'リトアニア語', + 'sr_Latn': 'litvanski', + 'sv': 'litauiska', + 'da': 'litauisk', + 'id': 'Lituania', + 'fr': 'lituanien', + 'es': 'lituano', + 'nn': 'litauisk', + 'en': 'Lithuanian', + }, + 'nb': { + '_native': 'norsk bokmål', + 'zh_CN': '书面挪威语', + 'is': 'norskt bókmál', + 'et': 'norra bokmål', + 'sr': 'норвешки букмол', + 'nl': 'Noors - Bokmål', + 'sl': 'knjižna norveščina', + 'fa': 'نروژی بوک‌مُل', + 'zh_TW': '書面挪威文', + 'bg': 'норвежки (букмол)', + 'gl': 'noruegués bokmål', + 'lt': 'norvegų bukmolas', + 'eu': 'bokmål (norvegiera)', + 'he': 'נורווגית ספרותית', + 'pl': 'norweski (bokmål)', + 'sk': 'nórčina (bokmal)', + 'cs': 'norština (bokmål)', + 'ka': 'ნორვეგიული ბუკმოლი', + 'de': 'Norwegisch (Bokmål)', + 'hu': 'norvég (bokmål)', + 'ca': 'noruec bokmål', + 'ar': 'النرويجية بوكمال', + 'ro': 'norvegiană bokmål', + 'fo': 'norskt bókmál', + 'tr': 'Norveççe Bokmål', + 'nb': 'norsk bokmål', + 'pt': 'bokmål norueguês', + 'it': 'norvegese bokmål', + 'uk': 'норвезька (букмол)', + 'fi': 'norjan bokmål', + 'ko': '노르웨이어(보크말)', + 'hr': 'norveški bokmål', + 'vi': 'Tiếng Na Uy (Bokmål)', + 'pt_BR': 'bokmål norueguês', + 'el': 'Νορβηγικά Μποκμάλ', + 'ru': 'норвежский букмол', + 'eo': 'dannorvega', + 'ja': 'ノルウェー語(ブークモール)', + 'sr_Latn': 'norveški bukmol', + 'sv': 'norskt bokmål', + 'da': 'bokmål', + 'id': 'Bokmål Norwegia', + 'fr': 'norvégien bokmål', + 'es': 'noruego bokmal', + 'nn': 'norsk bokmål', + 'en': 'Norwegian Bokmål', + }, + 'nl': { + '_native': 'Nederlands', + 'zh_CN': '荷兰语', + 'is': 'hollenska', + 'et': 'hollandi', + 'sr': 'холандски', + 'nl': 'Nederlands', + 'sl': 'nizozemščina', + 'fa': 'هلندی', + 'zh_TW': '荷蘭文', + 'bg': 'нидерландски', + 'gl': 'neerlandés', + 'lt': 'olandų', + 'eu': 'nederlandera', + 'he': 'הולנדית', + 'pl': 'niderlandzki', + 'sk': 'holandčina', + 'cs': 'nizozemština', + 'ka': 'ნიდერლანდური', + 'de': 'Niederländisch', + 'hu': 'holland', + 'ca': 'neerlandès', + 'ar': 'الهولندية', + 'ro': 'neerlandeză', + 'fo': 'hálendskt', + 'tr': 'Felemenkçe', + 'nb': 'nederlandsk', + 'pt': 'holandês', + 'it': 'olandese', + 'uk': 'нідерландська', + 'fi': 'hollanti', + 'ko': '네덜란드어', + 'hr': 'nizozemski', + 'vi': 'Tiếng Hà Lan', + 'pt_BR': 'holandês', + 'el': 'Ολλανδικά', + 'ru': 'нидерландский', + 'eo': 'nederlanda', + 'ja': 'オランダ語', + 'sr_Latn': 'holandski', + 'sv': 'nederländska', + 'da': 'nederlandsk', + 'id': 'Belanda', + 'fr': 'néerlandais', + 'es': 'neerlandés', + 'nn': 'nederlandsk', + 'en': 'Dutch', + }, + 'nn': { + '_native': 'norsk nynorsk', + 'zh_CN': '挪威尼诺斯克语', + 'is': 'nýnorska', + 'et': 'uusnorra', + 'sr': 'норвешки нинорск', + 'nl': 'Noors - Nynorsk', + 'sl': 'novonorveščina', + 'fa': 'نروژی نی‌نُشک', + 'zh_TW': '新挪威文', + 'bg': 'норвежки (нюношк)', + 'gl': 'noruegués nynorsk', + 'lt': 'naujoji norvegų', + 'eu': 'nynorsk (norvegiera)', + 'he': 'נורווגית חדשה', + 'pl': 'norweski (nynorsk)', + 'sk': 'nórčina (nynorsk)', + 'cs': 'norština (nynorsk)', + 'ka': 'ნორვეგიული ნიუნორსკი', + 'de': 'Norwegisch (Nynorsk)', + 'hu': 'norvég (nynorsk)', + 'ca': 'noruec nynorsk', + 'ar': 'النرويجية نينورسك', + 'ro': 'norvegiană nynorsk', + 'fo': 'nýnorskt', + 'tr': 'Norveççe Nynorsk', + 'nb': 'norsk nynorsk', + 'pt': 'nynorsk norueguês', + 'it': 'norvegese nynorsk', + 'uk': 'норвезька (нюношк)', + 'fi': 'norjan nynorsk', + 'ko': '노르웨이어(니노르스크)', + 'hr': 'norveški nynorsk', + 'vi': 'Tiếng Na Uy (Nynorsk)', + 'pt_BR': 'nynorsk norueguês', + 'el': 'Νορβηγικά Νινόρσκ', + 'ru': 'нюнорск', + 'eo': 'novnorvega', + 'ja': 'ノルウェー語(ニーノシュク)', + 'sr_Latn': 'norveški ninorsk', + 'sv': 'nynorska', + 'da': 'nynorsk', + 'id': 'Nynorsk Norwegia', + 'fr': 'norvégien nynorsk', + 'es': 'noruego nynorsk', + 'nn': 'norsk nynorsk', + 'en': 'Norwegian Nynorsk', + }, + 'pl': { + '_native': 'polski', + 'zh_CN': '波兰语', + 'is': 'pólska', + 'et': 'poola', + 'sr': 'пољски', + 'nl': 'Pools', + 'sl': 'poljščina', + 'fa': 'لهستانی', + 'zh_TW': '波蘭文', + 'bg': 'полски', + 'gl': 'polaco', + 'lt': 'lenkų', + 'eu': 'poloniera', + 'he': 'פולנית', + 'pl': 'polski', + 'sk': 'poľština', + 'cs': 'polština', + 'ka': 'პოლონური', + 'de': 'Polnisch', + 'hu': 'lengyel', + 'ca': 'polonès', + 'ar': 'البولندية', + 'ro': 'poloneză', + 'fo': 'pólskt', + 'tr': 'Lehçe', + 'nb': 'polsk', + 'pt': 'polonês', + 'it': 'polacco', + 'uk': 'польська', + 'fi': 'puola', + 'ko': '폴란드어', + 'hr': 'poljski', + 'vi': 'Tiếng Ba Lan', + 'pt_BR': 'polonês', + 'el': 'Πολωνικά', + 'ru': 'польский', + 'eo': 'pola', + 'ja': 'ポーランド語', + 'sr_Latn': 'poljski', + 'sv': 'polska', + 'da': 'polsk', + 'id': 'Polski', + 'fr': 'polonais', + 'es': 'polaco', + 'nn': 'polsk', + 'en': 'Polish', + }, + 'pt': { + '_native': 'português', + 'zh_CN': '葡萄牙语', + 'is': 'portúgalska', + 'et': 'portugali', + 'sr': 'португалски', + 'nl': 'Portugees', + 'sl': 'portugalščina', + 'fa': 'پرتغالی', + 'zh_TW': '葡萄牙文', + 'bg': 'португалски', + 'gl': 'portugués', + 'lt': 'portugalų', + 'eu': 'portugesa', + 'he': 'פורטוגזית', + 'pl': 'portugalski', + 'sk': 'portugalčina', + 'cs': 'portugalština', + 'ka': 'პორტუგალიური', + 'de': 'Portugiesisch', + 'hu': 'portugál', + 'ca': 'portuguès', + 'ar': 'البرتغالية', + 'ro': 'portugheză', + 'fo': 'portugiskiskt', + 'tr': 'Portekizce', + 'nb': 'portugisisk', + 'pt': 'português', + 'it': 'portoghese', + 'uk': 'португальська', + 'fi': 'portugali', + 'ko': '포르투갈어', + 'hr': 'portugalski', + 'vi': 'Tiếng Bồ Đào Nha', + 'pt_BR': 'português', + 'el': 'Πορτογαλικά', + 'ru': 'португальский', + 'eo': 'portugala', + 'ja': 'ポルトガル語', + 'sr_Latn': 'portugalski', + 'sv': 'portugisiska', + 'da': 'portugisisk', + 'id': 'Portugis', + 'fr': 'portugais', + 'es': 'portugués', + 'nn': 'portugisisk', + 'en': 'Portuguese', + }, + 'pt_BR': { + '_native': 'português (Brasil)', + 'zh_CN': '葡萄牙语 (巴西)', + 'is': 'portúgalska (Brasilía)', + 'et': 'portugali (Brasiilia)', + 'sr': 'португалски (Бразил)', + 'nl': 'Portugees (Brazilië)', + 'sl': 'portugalščina (Brazilija)', + 'fa': 'پرتغالی (برزیل)', + 'zh_TW': '葡萄牙文 (巴西)', + 'bg': 'португалски (Бразилия)', + 'gl': 'portugués (Brasil)', + 'lt': 'portugalų (Brazilija)', + 'eu': 'portugesa (Brasil)', + 'he': 'פורטוגזית (ברזיל)', + 'pl': 'portugalski (Brazylia)', + 'sk': 'portugalčina (Brazília)', + 'cs': 'portugalština (Brazílie)', + 'ka': 'პორტუგალიური (ბრაზილია)', + 'de': 'Portugiesisch (Brasilien)', + 'hu': 'portugál (Brazília)', + 'ca': 'portuguès (Brasil)', + 'ar': 'البرتغالية (البرازيل)', + 'ro': 'portugheză (Brazilia)', + 'fo': 'portugiskiskt (Brasil)', + 'tr': 'Portekizce (Brezilya)', + 'nb': 'portugisisk (Brasil)', + 'pt': 'português (Brasil)', + 'it': 'portoghese (Brasile)', + 'uk': 'португальська (Бразилія)', + 'fi': 'portugali (Brasilia)', + 'ko': '포르투갈어 (브라질)', + 'hr': 'portugalski (Brazil)', + 'vi': 'Tiếng Bồ Đào Nha (Brazil)', + 'pt_BR': 'português (Brasil)', + 'el': 'Πορτογαλικά (Βραζιλία)', + 'ru': 'португальский (Бразилия)', + 'eo': 'portugala (Brazilo)', + 'ja': 'ポルトガル語 (ブラジル)', + 'sr_Latn': 'portugalski (Brazil)', + 'sv': 'portugisiska (Brasilien)', + 'da': 'portugisisk (Brasilien)', + 'id': 'Portugis (Brasil)', + 'fr': 'portugais (Brésil)', + 'es': 'portugués (Brasil)', + 'nn': 'portugisisk (Brasil)', + 'en': 'Portuguese (Brazil)', + }, + 'ro': { + '_native': 'română', + 'zh_CN': '罗马尼亚语', + 'is': 'rúmenska', + 'et': 'rumeenia', + 'sr': 'румунски', + 'nl': 'Roemeens', + 'sl': 'romunščina', + 'fa': 'رومانیایی', + 'zh_TW': '羅馬尼亞文', + 'bg': 'румънски', + 'gl': 'romanés', + 'lt': 'rumunų', + 'eu': 'errumaniera', + 'he': 'רומנית', + 'pl': 'rumuński', + 'sk': 'rumunčina', + 'cs': 'rumunština', + 'ka': 'რუმინული', + 'de': 'Rumänisch', + 'hu': 'román', + 'ca': 'romanès', + 'ar': 'الرومانية', + 'ro': 'română', + 'fo': 'rumenskt', + 'tr': 'Rumence', + 'nb': 'rumensk', + 'pt': 'romeno', + 'it': 'rumeno', + 'uk': 'румунська', + 'fi': 'romania', + 'ko': '루마니아어', + 'hr': 'rumunjski', + 'vi': 'Tiếng Romania', + 'pt_BR': 'romeno', + 'el': 'Ρουμανικά', + 'ru': 'румынский', + 'eo': 'rumana', + 'ja': 'ルーマニア語', + 'sr_Latn': 'rumunski', + 'sv': 'rumänska', + 'da': 'rumænsk', + 'id': 'Rumania', + 'fr': 'roumain', + 'es': 'rumano', + 'nn': 'rumensk', + 'en': 'Romanian', + }, + 'ru': { + '_native': 'русский', + 'zh_CN': '俄语', + 'is': 'rússneska', + 'et': 'vene', + 'sr': 'руски', + 'nl': 'Russisch', + 'sl': 'ruščina', + 'fa': 'روسی', + 'zh_TW': '俄文', + 'bg': 'руски', + 'gl': 'ruso', + 'lt': 'rusų', + 'eu': 'errusiera', + 'he': 'רוסית', + 'pl': 'rosyjski', + 'sk': 'ruština', + 'cs': 'ruština', + 'ka': 'რუსული', + 'de': 'Russisch', + 'hu': 'orosz', + 'ca': 'rus', + 'ar': 'الروسية', + 'ro': 'rusă', + 'fo': 'russiskt', + 'tr': 'Rusça', + 'nb': 'russisk', + 'pt': 'russo', + 'it': 'russo', + 'uk': 'російська', + 'fi': 'venäjä', + 'ko': '러시아어', + 'hr': 'ruski', + 'vi': 'Tiếng Nga', + 'pt_BR': 'russo', + 'el': 'Ρωσικά', + 'ru': 'русский', + 'eo': 'rusa', + 'ja': 'ロシア語', + 'sr_Latn': 'ruski', + 'sv': 'ryska', + 'da': 'russisk', + 'id': 'Rusia', + 'fr': 'russe', + 'es': 'ruso', + 'nn': 'russisk', + 'en': 'Russian', + }, + 'sk': { + '_native': 'slovenčina', + 'zh_CN': '斯洛伐克语', + 'is': 'slóvakíska', + 'et': 'slovaki', + 'sr': 'словачки', + 'nl': 'Slowaaks', + 'sl': 'slovaščina', + 'fa': 'اسلواکی', + 'zh_TW': '斯洛伐克文', + 'bg': 'словашки', + 'gl': 'eslovaco', + 'lt': 'slovakų', + 'eu': 'eslovakiera', + 'he': 'סלובקית', + 'pl': 'słowacki', + 'sk': 'slovenčina', + 'cs': 'slovenština', + 'ka': 'სლოვაკური', + 'de': 'Slowakisch', + 'hu': 'szlovák', + 'ca': 'eslovac', + 'ar': 'السلوفاكية', + 'ro': 'slovacă', + 'fo': 'slovakiskt', + 'tr': 'Slovakça', + 'nb': 'slovakisk', + 'pt': 'eslovaco', + 'it': 'slovacco', + 'uk': 'словацька', + 'fi': 'slovakki', + 'ko': '슬로바키아어', + 'hr': 'slovački', + 'vi': 'Tiếng Slovak', + 'pt_BR': 'eslovaco', + 'el': 'Σλοβακικά', + 'ru': 'словацкий', + 'eo': 'slovaka', + 'ja': 'スロバキア語', + 'sr_Latn': 'slovački', + 'sv': 'slovakiska', + 'da': 'slovakisk', + 'id': 'Slovak', + 'fr': 'slovaque', + 'es': 'eslovaco', + 'nn': 'slovakisk', + 'en': 'Slovak', + }, + 'sl': { + '_native': 'slovenščina', + 'zh_CN': '斯洛文尼亚语', + 'is': 'slóvenska', + 'et': 'sloveeni', + 'sr': 'словеначки', + 'nl': 'Sloveens', + 'sl': 'slovenščina', + 'fa': 'اسلوونیایی', + 'zh_TW': '斯洛維尼亞文', + 'bg': 'словенски', + 'gl': 'esloveno', + 'lt': 'slovėnų', + 'eu': 'esloveniera', + 'he': 'סלובנית', + 'pl': 'słoweński', + 'sk': 'slovinčina', + 'cs': 'slovinština', + 'ka': 'სლოვენური', + 'de': 'Slowenisch', + 'hu': 'szlovén', + 'ca': 'eslovè', + 'ar': 'السلوفانية', + 'ro': 'slovenă', + 'fo': 'slovenskt', + 'tr': 'Slovence', + 'nb': 'slovensk', + 'pt': 'esloveno', + 'it': 'sloveno', + 'uk': 'словенська', + 'fi': 'sloveeni', + 'ko': '슬로베니아어', + 'hr': 'slovenski', + 'vi': 'Tiếng Slovenia', + 'pt_BR': 'esloveno', + 'el': 'Σλοβενικά', + 'ru': 'словенский', + 'eo': 'slovena', + 'ja': 'スロベニア語', + 'sr_Latn': 'slovenački', + 'sv': 'slovenska', + 'da': 'slovensk', + 'id': 'Slovenia', + 'fr': 'slovène', + 'es': 'esloveno', + 'nn': 'slovensk', + 'en': 'Slovenian', + }, + 'sr': { + '_native': 'српски', + 'zh_CN': '塞尔维亚语', + 'is': 'serbneska', + 'et': 'serbia', + 'sr': 'српски', + 'nl': 'Servisch', + 'sl': 'srbščina', + 'fa': 'صربی', + 'zh_TW': '塞爾維亞文', + 'bg': 'сръбски', + 'gl': 'serbio', + 'lt': 'serbų', + 'eu': 'serbiera', + 'he': 'סרבית', + 'pl': 'serbski', + 'sk': 'srbčina', + 'cs': 'srbština', + 'ka': 'სერბული', + 'de': 'Serbisch', + 'hu': 'szerb', + 'ca': 'serbi', + 'ar': 'الصربية', + 'ro': 'sârbă', + 'fo': 'serbiskt', + 'tr': 'Sırpça', + 'nb': 'serbisk', + 'pt': 'sérvio', + 'it': 'serbo', + 'uk': 'сербська', + 'fi': 'serbia', + 'ko': '세르비아어', + 'hr': 'srpski', + 'vi': 'Tiếng Serbia', + 'pt_BR': 'sérvio', + 'el': 'Σερβικά', + 'ru': 'сербский', + 'eo': 'serba', + 'ja': 'セルビア語', + 'sr_Latn': 'srpski', + 'sv': 'serbiska', + 'da': 'serbisk', + 'id': 'Serbia', + 'fr': 'serbe', + 'es': 'serbio', + 'nn': 'serbisk', + 'en': 'Serbian', + }, + 'sr_Latn': { + '_native': 'srpski (latinica)', + 'zh_CN': '塞尔维亚语 (拉丁文)', + 'is': 'serbneska (latneskt)', + 'et': 'serbia (ladina)', + 'sr': 'српски (латиница)', + 'nl': 'Servisch (Latijns)', + 'sl': 'srbščina (latinica)', + 'fa': 'صربی (لاتین)', + 'zh_TW': '塞爾維亞文 (拉丁字母)', + 'bg': 'сръбски (латиница)', + 'gl': 'serbio (latino)', + 'lt': 'serbų (lotynų)', + 'eu': 'serbiera (latinoa)', + 'he': 'סרבית (לטיני)', + 'pl': 'serbski (łacińskie)', + 'sk': 'srbčina (latinka)', + 'cs': 'srbština (latinka)', + 'ka': 'სერბული (ლათინური)', + 'de': 'Serbisch (Lateinisch)', + 'hu': 'szerb (Latin)', + 'ca': 'serbi (llatí)', + 'ar': 'الصربية (اللاتينية)', + 'ro': 'sârbă (latină)', + 'fo': 'serbiskt (latínskt)', + 'tr': 'Sırpça (Latin)', + 'nb': 'serbisk (latinsk)', + 'pt': 'sérvio (latim)', + 'it': 'serbo (latino)', + 'uk': 'сербська (латиниця)', + 'fi': 'serbia (latinalainen)', + 'ko': '세르비아어 (로마자)', + 'hr': 'srpski (latinica)', + 'vi': 'Tiếng Serbia (Chữ La tinh)', + 'pt_BR': 'sérvio (latim)', + 'el': 'Σερβικά (Λατινικό)', + 'ru': 'сербский (латиница)', + 'eo': 'serba (latina)', + 'ja': 'セルビア語 (ラテン文字)', + 'sr_Latn': 'srpski (latinica)', + 'sv': 'serbiska (latinska)', + 'da': 'serbisk (latinsk)', + 'id': 'Serbia (Latin)', + 'fr': 'serbe (latin)', + 'es': 'serbio (latino)', + 'nn': 'serbisk (latinsk)', + 'en': 'Serbian (Latin)', + }, + 'sv': { + '_native': 'svenska', + 'zh_CN': '瑞典语', + 'is': 'sænska', + 'et': 'rootsi', + 'sr': 'шведски', + 'nl': 'Zweeds', + 'sl': 'švedščina', + 'fa': 'سوئدی', + 'zh_TW': '瑞典文', + 'bg': 'шведски', + 'gl': 'sueco', + 'lt': 'švedų', + 'eu': 'suediera', + 'he': 'שוודית', + 'pl': 'szwedzki', + 'sk': 'švédčina', + 'cs': 'švédština', + 'ka': 'შვედური', + 'de': 'Schwedisch', + 'hu': 'svéd', + 'ca': 'suec', + 'ar': 'السويدية', + 'ro': 'suedeză', + 'fo': 'svenskt', + 'tr': 'İsveççe', + 'nb': 'svensk', + 'pt': 'sueco', + 'it': 'svedese', + 'uk': 'шведська', + 'fi': 'ruotsi', + 'ko': '스웨덴어', + 'hr': 'švedski', + 'vi': 'Tiếng Thụy Điển', + 'pt_BR': 'sueco', + 'el': 'Σουηδικά', + 'ru': 'шведский', + 'eo': 'sveda', + 'ja': 'スウェーデン語', + 'sr_Latn': 'švedski', + 'sv': 'svenska', + 'da': 'svensk', + 'id': 'Swedia', + 'fr': 'suédois', + 'es': 'sueco', + 'nn': 'svensk', + 'en': 'Swedish', + }, + 'tr': { + '_native': 'Türkçe', + 'zh_CN': '土耳其语', + 'is': 'tyrkneska', + 'et': 'türgi', + 'sr': 'турски', + 'nl': 'Turks', + 'sl': 'turščina', + 'fa': 'ترکی استانبولی', + 'zh_TW': '土耳其文', + 'bg': 'турски', + 'gl': 'turco', + 'lt': 'turkų', + 'eu': 'turkiera', + 'he': 'טורקית', + 'pl': 'turecki', + 'sk': 'turečtina', + 'cs': 'turečtina', + 'ka': 'თურქული', + 'de': 'Türkisch', + 'hu': 'török', + 'ca': 'turc', + 'ar': 'التركية', + 'ro': 'turcă', + 'fo': 'turkiskt', + 'tr': 'Türkçe', + 'nb': 'tyrkisk', + 'pt': 'turco', + 'it': 'turco', + 'uk': 'турецька', + 'fi': 'turkki', + 'ko': '튀르키예어', + 'hr': 'turski', + 'vi': 'Tiếng Thổ Nhĩ Kỳ', + 'pt_BR': 'turco', + 'el': 'Τουρκικά', + 'ru': 'турецкий', + 'eo': 'turka', + 'ja': 'トルコ語', + 'sr_Latn': 'turski', + 'sv': 'turkiska', + 'da': 'tyrkisk', + 'id': 'Turki', + 'fr': 'turc', + 'es': 'turco', + 'nn': 'tyrkisk', + 'en': 'Turkish', + }, + 'uk': { + '_native': 'українська', + 'zh_CN': '乌克兰语', + 'is': 'úkraínska', + 'et': 'ukraina', + 'sr': 'украјински', + 'nl': 'Oekraïens', + 'sl': 'ukrajinščina', + 'fa': 'اوکراینی', + 'zh_TW': '烏克蘭文', + 'bg': 'украински', + 'gl': 'ucraíno', + 'lt': 'ukrainiečių', + 'eu': 'ukrainera', + 'he': 'אוקראינית', + 'pl': 'ukraiński', + 'sk': 'ukrajinčina', + 'cs': 'ukrajinština', + 'ka': 'უკრაინული', + 'de': 'Ukrainisch', + 'hu': 'ukrán', + 'ca': 'ucraïnès', + 'ar': 'الأوكرانية', + 'ro': 'ucraineană', + 'fo': 'ukrainskt', + 'tr': 'Ukraynaca', + 'nb': 'ukrainsk', + 'pt': 'ucraniano', + 'it': 'ucraino', + 'uk': 'українська', + 'fi': 'ukraina', + 'ko': '우크라이나어', + 'hr': 'ukrajinski', + 'vi': 'Tiếng Ukraina', + 'pt_BR': 'ucraniano', + 'el': 'Ουκρανικά', + 'ru': 'украинский', + 'eo': 'ukraina', + 'ja': 'ウクライナ語', + 'sr_Latn': 'ukrajinski', + 'sv': 'ukrainska', + 'da': 'ukrainsk', + 'id': 'Ukraina', + 'fr': 'ukrainien', + 'es': 'ucraniano', + 'nn': 'ukrainsk', + 'en': 'Ukrainian', + }, + 'vi': { + '_native': 'Tiếng Việt', + 'zh_CN': '越南语', + 'is': 'víetnamska', + 'et': 'vietnami', + 'sr': 'вијетнамски', + 'nl': 'Vietnamees', + 'sl': 'vietnamščina', + 'fa': 'ویتنامی', + 'zh_TW': '越南文', + 'bg': 'виетнамски', + 'gl': 'vietnamita', + 'lt': 'vietnamiečių', + 'eu': 'vietnamera', + 'he': 'וייטנאמית', + 'pl': 'wietnamski', + 'sk': 'vietnamčina', + 'cs': 'vietnamština', + 'ka': 'ვიეტნამური', + 'de': 'Vietnamesisch', + 'hu': 'vietnámi', + 'ca': 'vietnamita', + 'ar': 'الفيتنامية', + 'ro': 'vietnameză', + 'fo': 'vjetnamesiskt', + 'tr': 'Vietnamca', + 'nb': 'vietnamesisk', + 'pt': 'vietnamita', + 'it': 'vietnamita', + 'uk': 'вʼєтнамська', + 'fi': 'vietnam', + 'ko': '베트남어', + 'hr': 'vijetnamski', + 'vi': 'Tiếng Việt', + 'pt_BR': 'vietnamita', + 'el': 'Βιετναμικά', + 'ru': 'вьетнамский', + 'eo': 'vjetnama', + 'ja': 'ベトナム語', + 'sr_Latn': 'vijetnamski', + 'sv': 'vietnamesiska', + 'da': 'vietnamesisk', + 'id': 'Vietnam', + 'fr': 'vietnamien', + 'es': 'vietnamita', + 'nn': 'vietnamesisk', + 'en': 'Vietnamese', + }, + 'zh_CN': { + '_native': '中文 (简体, 中国)', + 'zh_CN': '中文 (简体, 中国)', + 'is': 'kínverska (einfaldað, Kína)', + 'et': 'hiina (lihtsustatud, Hiina)', + 'sr': 'кинески (поједностављено кинеско писмо, Кина)', + 'nl': 'Chinees (vereenvoudigd, China)', + 'sl': 'kitajščina (poenostavljena pisava, Kitajska)', + 'fa': 'چینی (ساده‌شده, چین)', + 'zh_TW': '中文 (簡體, 中國)', + 'bg': 'китайски (опростена, Китай)', + 'gl': 'chinés (simplificado, China)', + 'lt': 'kinų (supaprastinti, Kinija)', + 'eu': 'txinera (sinplifikatua, Txina)', + 'he': 'סינית (פשוט, סין)', + 'pl': 'chiński (uproszczone, Chiny)', + 'sk': 'čínština (zjednodušené, Čína)', + 'cs': 'čínština (zjednodušené, Čína)', + 'ka': 'ჩინური (გამარტივებული, ჩინეთი)', + 'de': 'Chinesisch (Vereinfacht, China)', + 'hu': 'kínai (Egyszerűsített, Kína)', + 'ca': 'xinès (simplificat, Xina)', + 'ar': 'الصينية (المبسطة, الصين)', + 'ro': 'chineză (simplificată, China)', + 'fo': 'kinesiskt (einkult, Kina)', + 'tr': 'Çince (Basitleştirilmiş, Çin)', + 'nb': 'kinesisk (forenklet, Kina)', + 'pt': 'chinês (simplificado, China)', + 'it': 'cinese (semplificato, Cina)', + 'uk': 'китайська (спрощена, Китай)', + 'fi': 'kiina (yksinkertaistettu, Kiina)', + 'ko': '중국어 (간체, 중국)', + 'hr': 'kineski (pojednostavljeno pismo, Kina)', + 'vi': 'Tiếng Trung (Giản thể, Trung Quốc)', + 'pt_BR': 'chinês (simplificado, China)', + 'el': 'Κινεζικά (Απλοποιημένο, Κίνα)', + 'ru': 'китайский (упрощенная, Китай)', + 'eo': 'ĉina (simpligita, Ĉinujo)', + 'ja': '中国語 (簡体字, 中国)', + 'sr_Latn': 'kineski (pojednostavljeno kinesko pismo, Kina)', + 'sv': 'kinesiska (förenklad, Kina)', + 'da': 'kinesisk (forenklet, Kina)', + 'id': 'Tionghoa (Sederhana, Tiongkok)', + 'fr': 'chinois (simplifié, Chine)', + 'es': 'chino (simplificado, China)', + 'nn': 'kinesisk (forenkla, Kina)', + 'en': 'Chinese (Simplified, China)', + }, + 'zh_TW': { + '_native': '中文 (繁體, 台灣)', + 'zh_CN': '中文 (繁体, 台湾)', + 'is': 'kínverska (hefðbundið, Taívan)', + 'et': 'hiina (traditsiooniline, Taiwan)', + 'sr': 'кинески (традиционално кинеско писмо, Тајван)', + 'nl': 'Chinees (traditioneel, Taiwan)', + 'sl': 'kitajščina (tradicionalna pisava, Tajvan)', + 'fa': 'چینی (سنتی, تایوان)', + 'zh_TW': '中文 (繁體, 台灣)', + 'bg': 'китайски (традиционна, Тайван)', + 'gl': 'chinés (tradicional, Taiwán)', + 'lt': 'kinų (tradiciniai, Taivanas)', + 'eu': 'txinera (tradizionala, Taiwan)', + 'he': 'סינית (מסורתי, טייוואן)', + 'pl': 'chiński (tradycyjne, Tajwan)', + 'sk': 'čínština (tradičné, Taiwan)', + 'cs': 'čínština (tradiční, Tchaj-wan)', + 'ka': 'ჩინური (ტრადიციული, ტაივანი)', + 'de': 'Chinesisch (Traditionell, Taiwan)', + 'hu': 'kínai (Hagyományos, Tajvan)', + 'ca': 'xinès (tradicional, Taiwan)', + 'ar': 'الصينية (التقليدية, تايوان)', + 'ro': 'chineză (tradițională, Taiwan)', + 'fo': 'kinesiskt (vanligt, Taivan)', + 'tr': 'Çince (Geleneksel, Tayvan)', + 'nb': 'kinesisk (tradisjonell, Taiwan)', + 'pt': 'chinês (tradicional, Taiwan)', + 'it': 'cinese (tradizionale, Taiwan)', + 'uk': 'китайська (традиційна, Тайвань)', + 'fi': 'kiina (perinteinen, Taiwan)', + 'ko': '중국어 (번체, 대만)', + 'hr': 'kineski (tradicionalno pismo, Tajvan)', + 'vi': 'Tiếng Trung (Phồn thể, Đài Loan)', + 'pt_BR': 'chinês (tradicional, Taiwan)', + 'el': 'Κινεζικά (Παραδοσιακό, Ταϊβάν)', + 'ru': 'китайский (традиционная, Тайвань)', + 'eo': 'ĉina (tradicia, Tajvano)', + 'ja': '中国語 (繁体字, 台湾)', + 'sr_Latn': 'kineski (tradicionalno kinesko pismo, Tajvan)', + 'sv': 'kinesiska (traditionell, Taiwan)', + 'da': 'kinesisk (traditionelt, Taiwan)', + 'id': 'Tionghoa (Tradisional, Taiwan)', + 'fr': 'chinois (traditionnel, Taïwan)', + 'es': 'chino (tradicional, Taiwán)', + 'nn': 'kinesisk (tradisjonell, Taiwan)', + 'en': 'Chinese (Traditional, Taiwan)', + }, +} + + +completeness = { + 'ar': 45, + 'bg': 53, + 'ca': 81, + 'cs': 98, + 'da': 98, + 'de': 98, + 'el': 88, + 'en': 100, + 'eo': 98, + 'es': 98, + 'et': 98, + 'eu': 98, + 'fa': 71, + 'fi': 86, + 'fo': 18, + 'fr': 98, + 'gl': 98, + 'he': 80, + 'hr': 24, + 'hu': 98, + 'id': 96, + 'is': 44, + 'it': 98, + 'ja': 98, + 'ka': 22, + 'ko': 54, + 'lt': 59, + 'nb': 61, + 'nl': 98, + 'nn': 46, + 'pl': 98, + 'pt': 98, + 'pt_BR': 61, + 'ro': 98, + 'ru': 98, + 'sk': 54, + 'sl': 65, + 'sr': 57, + 'sr_Latn': 54, + 'sv': 98, + 'tr': 98, + 'uk': 98, + 'vi': 35, + 'zh_CN': 98, + 'zh_TW': 80, +} diff --git a/common/logger.py b/common/logger.py index 7ba1f84d3..d507d74cc 100644 --- a/common/logger.py +++ b/common/logger.py @@ -1,101 +1,122 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import syslog import os +import pwd import sys import atexit - -import tools import bcolors -DEBUG = False -APP_NAME = 'backintime' -def openlog(): - name = os.getenv('LOGNAME', 'unknown') - syslog.openlog("%s (%s/1)" %(APP_NAME, name)) +DEBUG = False # Set to "True" when passing "--debug" as cmd arg +SYSLOG_IDENTIFIER = 'backintime' +SYSLOG_MESSAGE_PREFIX = '' +USER = pwd.getpwuid(os.getuid()).pw_name + +# Labels for the syslog levels +_level_names = { + syslog.LOG_INFO: 'INFO', + syslog.LOG_WARNING: 'WARNING', + syslog.LOG_ERR: 'ERROR', + syslog.LOG_CRIT: 'CRITICAL', + syslog.LOG_DEBUG: 'DEBUG', +} + +syslog_id_suffix = '' + + +def openlog(suffix: str): + """Initialize the BIT logger system using syslog. + + Attention: Call it in each sub process that uses logging. + """ + global syslog_id_suffix + syslog_id_suffix = suffix + syslog.openlog(SYSLOG_IDENTIFIER) atexit.register(closelog) -def changeProfile(profile_id): - name = os.getenv('LOGNAME', 'unknown') - syslog.openlog("%s (%s/%s)" %(APP_NAME, name, profile_id)) + +def changeProfile(profile_id, profile_name): + global SYSLOG_MESSAGE_PREFIX + SYSLOG_MESSAGE_PREFIX = f'{profile_name}({profile_id}) :: ' + def closelog(): syslog.closelog() -def error(msg , parent = None, traceDepth = 0): + +def _do_syslog(message: str, level: int) -> str: + syslog.syslog(level, '{}: {}{}{}'.format( + _level_names[level], + f'{USER} ' if DEBUG else '', + SYSLOG_MESSAGE_PREFIX, + message + )) + + +def critical(msg, parent=None, traceDepth=0): if DEBUG: - msg = '%s %s' %(_debugHeader(parent, traceDepth), msg) - print('%sERROR%s: %s' %(bcolors.FAIL, bcolors.ENDC, msg), file=sys.stderr) - for line in tools.wrapLine(msg): - syslog.syslog(syslog.LOG_ERR, 'ERROR: ' + line) + msg = _debugHeader(parent, traceDepth) + ' ' + msg + + print(f'{bcolors.CRITICAL}CRITICAL{bcolors.ENDC}: {msg}', file=sys.stderr) + + _do_syslog(msg, syslog.LOG_CRIT) -def warning(msg , parent = None, traceDepth = 0): + +def error(msg, parent=None, traceDepth=0): if DEBUG: - msg = '%s %s' %(_debugHeader(parent, traceDepth), msg) - print('%sWARNING%s: %s' %(bcolors.WARNING, bcolors.ENDC, msg), file=sys.stderr) - for line in tools.wrapLine(msg): - syslog.syslog(syslog.LOG_WARNING, 'WARNING: ' + line) + msg = _debugHeader(parent, traceDepth) + ' ' + msg + + print(f'{bcolors.FAIL}ERROR{bcolors.ENDC}: {msg}', file=sys.stderr) + + _do_syslog(msg, syslog.LOG_ERR) + -def info(msg , parent = None, traceDepth = 0): +def warning(msg, parent=None, traceDepth=0): if DEBUG: - msg = '%s %s' %(_debugHeader(parent, traceDepth), msg) - print('%sINFO%s: %s' %(bcolors.OKGREEN, bcolors.ENDC, msg), file=sys.stdout) - for line in tools.wrapLine(msg): - syslog.syslog(syslog.LOG_INFO, 'INFO: ' + line) + msg = _debugHeader(parent, traceDepth) + ' ' + msg -def debug(msg, parent = None, traceDepth = 0): + print(f'{bcolors.WARNING}WARNING{bcolors.ENDC}: {msg}', file=sys.stderr) + + _do_syslog(msg, syslog.LOG_WARNING) + + +def info(msg, parent=None, traceDepth=0): if DEBUG: - msg = '%s %s' %(_debugHeader(parent, traceDepth), msg) - print('%sDEBUG%s: %s' %(bcolors.OKBLUE, bcolors.ENDC, msg), file = sys.stdout) - for line in tools.wrapLine(msg): - syslog.syslog(syslog.LOG_DEBUG, 'DEBUG: %s' %line) + msg = _debugHeader(parent, traceDepth) + ' ' + msg + + print(f'{bcolors.OKGREEN}INFO{bcolors.ENDC}: {msg}', file=sys.stderr) + + _do_syslog(msg, syslog.LOG_INFO) -def deprecated(parent = None): - frame = sys._getframe(1) - fdir, fname = os.path.split(frame.f_code.co_filename) - fmodule = os.path.basename(fdir) - line = frame.f_lineno - if parent: - fclass = '%s.' %parent.__class__.__name__ - else: - fclass = '' - func = frame.f_code.co_name - frameCaller = sys._getframe(2) - fdirCaller, fnameCaller = os.path.split(frameCaller.f_code.co_filename) - fmoduleCaller = os.path.basename(fdirCaller) - lineCaller = frameCaller.f_lineno +def debug(msg, parent=None, traceDepth=0): + if not DEBUG: + return - msg = '%s/%s:%s %s%s called from ' %(fmodule, fname, line, fclass, func) - msgCaller = '%s/%s:%s' %(fmoduleCaller, fnameCaller, lineCaller) + msg = _debugHeader(parent, traceDepth) + ' ' + msg + + print(f'{bcolors.OKBLUE}DEBUG{bcolors.ENDC}: {msg}', file=sys.stderr) + + _do_syslog(msg, syslog.LOG_DEBUG) - print('%sDEPRECATED%s: %s%s%s%s' %(bcolors.WARNING, bcolors.ENDC, msg, bcolors.OKBLUE, msgCaller, bcolors.ENDC), file=sys.stderr) - syslog.syslog(syslog.LOG_WARNING, 'DEPRECATED: %s%s' %(msg, msgCaller)) def _debugHeader(parent, traceDepth): frame = sys._getframe(2 + traceDepth) - fdir, fname = os.path.split(frame.f_code.co_filename) - fmodule = os.path.basename(fdir) line = frame.f_lineno - if parent: - fclass = '%s.' %parent.__class__.__name__ - else: - fclass = '' func = frame.f_code.co_name - return '[%s/%s:%s %s%s]' %(fmodule, fname, line, fclass, func) + + fdir, fname = os.path.split(frame.f_code.co_filename) + fmodule = os.path.basename(fdir) + + fclass = f'{parent.__class__.__name__}.' if parent else '' + + return f'[{syslog_id_suffix}::{fmodule}/{fname}:{line} {fclass}{func}]' diff --git a/common/man/C/backintime-askpass.1 b/common/man/C/backintime-askpass.1 deleted file mode 100644 index 8c5566e03..000000000 --- a/common/man/C/backintime-askpass.1 +++ /dev/null @@ -1,32 +0,0 @@ -.TH backintime-askpass 1 "Jan 2015" "version 1.3.1" "USER COMMANDS" -.SH NAME -backintime-askpass \- a simple backup tool for Linux. -.PP -This is the command line tool for piping passwords into ssh/sshfs and encfs. -.SH SYNOPSIS -.B backintime-askpass - -.SH DESCRIPTION -Back In Time is a simple backup tool for Linux. This is a helper tool for -piping passwords into ssh/sshfs and encfs. Options will will be read from -environ variables. It doesn't provide any useful enduser service. - - -.SH ENVIRON -.TP -ASKPASS_PROFILE_ID -Back In Time Profile-ID. -.TP -ASKPASS_MODE -Backup mode (or backend). Take a look at 'man backintime-config' -section \fIprofile.snapshots.mode\fR -.TP -ASKPASS_TEMP -Temp FIFO socket used to pipe the password - -.SH SEE ALSO -backintime, backintime-config. -.PP -Back In Time also has a website: https://github.com/bit-team/backintime -.SH AUTHOR -This manual page was written by BIT Team(). diff --git a/common/man/C/backintime.1 b/common/man/C/backintime.1 deleted file mode 100644 index 546882532..000000000 --- a/common/man/C/backintime.1 +++ /dev/null @@ -1,383 +0,0 @@ -.TH backintime 1 "Aug 2016" "version 1.3.1" "USER COMMANDS" -.SH NAME -backintime \- a simple backup tool for Linux. -.PP -This is the command line tool. -The graphical tool is backintime-qt. -.SH SYNOPSIS -.B backintime -[\-\-checksum] -[\-\-config PATH] -[\-\-debug] -[\-\-delete] -[\-\-help | \-h] -[\-\-keep\-mount] -[\-\-license] -[\-\-local\-backup | -\-\-no\-local\-backup] -[\-\-no\-crontab] -[\-\-only\-new] -[\-\-profile NAME | -\-\-profile\-id ID] -[\-\-quiet] -[\-\-share\-path PATH] -[\-\-version] - -{ backup | backup\-job | -benchmark-cipher [FILE-SIZE] | -check-config | -decode [PATH] | -last\-snapshot | last\-snapshot\-path | -pw\-cache [start|stop|restart|reload|status] | -remove[\-and\-do\-not\-ask\-again] [SNAPSHOT_ID] | -restore [WHAT [WHERE [SNAPSHOT_ID]]] | -shutdown | -smart\-remove | -snapshots\-list | snapshots\-list\-path | -snapshots\-path | -unmount } - -.SH DESCRIPTION -Back In Time is a simple backup tool for Linux. The backup is done by taking -snapshots of a specified set of folders. -.PP -All you have to do is configure: where to save snapshots, what folders to backup. -You can also specify a backup schedule: disabled, every 5 minutes, every -10 minutes, every hour, every day, every week, every month. To configure it use -one of the graphical interfaces available (backintime-gnome or backintime-kde4). -.PP -It acts as a 'user mode' backup tool. This means that you can backup/restore only -folders you have write access to (actually you can backup read\-only folders, -but you can't restore them). -.PP -If you want to run it as root you need to use 'sudo -i backintime'. -.PP -A new snapshot is created only if something changed since the last snapshot -(if any). -.PP -A snapshot contains all the files from the selected folders (except for exclude -patterns). In order to reduce disk space it use hard\-links (if possible) -between snapshots for unchanged files. This way a file of 10MiB, unchanged for -10 snapshots, will use only 10MiB on the disk. -.PP -When you restore a file 'A', if it already exists on the file system it will be -renamed to 'A.backup.currentdate'. -.PP -For automatic backup it use 'cron' so there is no need for a daemon, but 'cron' -must be running. -.SS Modes -.IP "\fILocal\fR" 4 -.RS -Store snapshots on local HDD's (internal or USB). The drive has to be mounted -before creating a new snapshot. -.RE -.IP "\fILocal encrypted\fR" 4 -.RS -Store encrypted snapshots on local HDD's (internal or USB). -Back In Time uses 'encfs' with standard configuration to encrypt all data. -Please take a look at \fIA NOTE ON SECURITY\fR. -.RE -.IP "\fISSH \fR" 4 -.RS -With Mode set to SSH you can store the backup on a remote host using the -SecureShellHost protocol (ssh). -The remote path will be mount local using sshfs to provide file-access for the -graphical interface and the backup process. -Rsync and other processes called during backup process will run directly on the -remote host using ssh. -.PP -To prepare your user account for ssh-mode you have to create a password-less -login to the remote host (for further information look at -http://www.debian-administration.org/articles/152). -Type in terminal 'ssh-keygen \-t rsa' hit enter for default path and enter a -passphrase for the private key. -.PP -Finally type 'ssh-copy-id \-i ~/.ssh/id_rsa.pub @' and enter -your password on remote host. -.PP -In Settingsdialog you need to set the host and remote user. If you enter a -relative path (no leading / ) it will start from remote users homedir. The -password has to be the passphrase for your private key. -.PP -.B Cipher -(the algorithm used to encrypt the data during transfer) -.br -To optimize performance you can choose the cipher used by ssh. Depending on your -environment you can have a massive speed increase compared to the default cipher. -.PP -\fIbenchmark\-cipher\fR will give you an overview over which cipher is the fastest -in your environment. -.PP -If the bottleneck of your environment is the hard-drive or the network you will -not see a big difference between the ciphers. In this case you should rather -stay on 'default'. -.PP -Please read security information about the cipher before using them in untrusted -networks (Wifi, Internet). Some of them (Arcfour, 3DES, ...) should be handled -as not secure anymore. -.PP -.B "Remote Host" -.br -If your remote host is an embedded Linux NAS or any other device with limited -functions, you could run into some problems caused by feature-less commands. -For example some devices may not have hardlink support for 'cp', 'chmod' -and 'rsync'. In this case it may help to install so-called Optware or Entware -on your device if available. -.PP -.B WARNING: THIS IS ONLY FOR EXPERIENCED USERS! -.br -If you don't know how to compile packages and how to modify a Linux system you -should NOT try to do this. There is a significant chance to break your device -and make it completely unusable with the following procedure. We will not take -any warranty for this. Make a backup of your device before proceed! -You have been warned! -.PP -You should install at least packages called 'bash', 'coreutils' and 'rsync'. -You will have to change users default shell from '/bin/sh' to '/opt/bin/bash' -in '/etc/passwd'. To add '/opt/bin:/opt/sbin:' to the start of the PATH environment -you can use 'Add prefix to SSH commands' in 'Expert Options' -with 'PATH=/opt/bin:/opt/sbin:\\$PATH'. -.PP -To check if it does work you can compare the output of '/bin/cp \-\-help' -and '/opt/bin/cp \-\-help'. If 'ssh @ cp \-\-help' called from -your PC will print the same as '/opt/bin/cp \-\-help' called on the -remote host (via interactive ssh session) you are ready to go. -.PP -If you have questions on how to install and configure the Optware please refer -to the community of your device. You can also take a look on Back In Time FAQ on -GitHub https://github.com/bit-team/backintime/wiki/FAQ -.PP -If you successfully modified your device to be able to make backups over ssh, -it would be nice if you write a 'How to' on Launchpad's Answers so we can add -this to the FAQ. -.RE -.IP "\fISSH encrypted\fR" 4 -.RS -Store encrypted snapshots on remote hosts using SSH. Backintime -uses 'encfs \-\-reverse' to mount the root filesystem '/'. Rsync will sync this -encrypted view of '/' to a remote host over SSH. All encoding will be done on -the local machine. So the password will never be exposed to the remote host and -you can use the (normally) more powerful processor in you local machine for -encryption instead of weak NAS CPU's. The downside on this -is 'encfs \-\-reverse' does not support 'Filename Initialization Vector Chaining' -and 'Per-File Initialization Vectors' from the standard configuration -(take a look at 'man encfs' for further information). Please take a look at -\fIA NOTE ON SECURITY\fR. -.PP -Because of all data is transferred encrypted the log output shows encrypted -filenames, too. In the Logview-Dialog you can use 'decode' option to decrypt -the paths automatically or you can use 'backintime decode' to manually -decrypt paths. Back In Time will show all snapshots decoded so you can browse -all files as normal. -.PP -Exclude does not support wildcards ('foo*', '[fF]oo', 'fo?') because after -encoding a file these wildcards can't match any more. Only separate asterisk -that match a full file or folder will work ('foo/*', 'foo/**/bar'). All other -excludes that have wildcards will be silently ignored. -.PP -Please refer to the 'SSH' section above for information on setting up the SSH -connection. -.RE -.SS Password -If 'Save Password to Keyring' is activated Back In Time will save the Password -into GnomeKeyring (Seahorse) or KDE-KWallet. Both are secure password storages -which encrypt the password with the users login-password. So they can only be -accessed if the user is logged in. -.PP -A backup cronjob during the user isn't logged in can not collect the password -from keyring. Also if the homedir is encrypted the keyring is not accessible -from cronjobs (even if the user is logged in). For these cases the password can -be cached in RAM. If 'Cache Password for Cron' is activated Back In Time will -start a small daemon in user-space which will collect the password from keyring -and provide them for cronjobs. They will never be written to the harddrive but -a user with root permissions could access the daemon and read the password. -.SS user-callback -During backup process the application can call a user callback at different steps. -This callback is "$XDG_CONFIG_HOME/backintime/user-callback" -(by default $XDG_CONFIG_HOME is ~/.config). -.PP -The first argument is the profile id (1=Main Profile, ...). -.PP -The second argument is the profile name. -.PP -The third argument is the reason: -.RS -.TP -1 -Backup process begins. -.TP -2 -Backup process ends. -.TP -3 -A new snapshot was taken. The extra arguments are snapshot ID and snapshot path. -.TP -4 -There was an error. The second argument is the error code. -.RS -Error codes: -.TP -1 -The application is not configured. -.TP -2 -A "take snapshot" process is already running. -.TP -3 -Can't find snapshots folder (is it on a removable drive ?). -.TP -4 -A snapshot for "now" already exist. -.RE -.TP -5 -On (graphical) App start. -.TP -6 -On (graphical) App close. -.TP -7 -Mount all necessary drives. -.TP -8 -Unmount all drives. -.SH OPTIONS -.TP -\-\-checksum -Force to use checksum for checking if files have been changed. This is the same -as 'Use checksum to detect changes' in Options. But you can use this to -periodically run checksums from cronjobs. Only valid with \fIbackup\fR, -\fIbackup-job\fR and \fIrestore\fR. -.TP -\-\-config PATH -Read config from PATH. Default = ~/.config/backintime/config -.TP ---debug -Show debug messages. -.TP ---delete -Restore and delete newer files which are not in the snapshot. -WARNING: deleting files in filesystem root could break your whole system!!! -Only valid with \fIrestore\fR. -.TP -\-h, \-\-help -Display a short help -.TP -\-\-keep\-mount -Don't unmount on exit. Only valid with \fIsnapshots\-path\fR, \fIsnapshots\-list\-path\fR and -\fIlast\-snapshot\-path\fR. -.TP -\-\-license -Show license -.TP ---local-backup -Create backup files before changing local files. -Only valid with \fIrestore\fR. -.TP ---no-crontab -Do not install crontab entries. -Only valid with \fIcheck-config\fR. -.TP ---no-local-backup -Temporary disable creation of backup files before changing local files. -Only valid with \fIrestore\fR. -.TP ---only-new -Only restore files which does not exist or are newer than those in destination. -Using "rsync --update" option. -Only valid with \fIrestore\fR. -.TP -\-\-profile NAME -Select profile by name -.TP -\-\-profile\-id ID -Select profile by id -.TP -\-\-quiet -Suppress status messages on standard output. -.TP -\-\-share\-path PATH -Write runtime data (locks, messages, log and mountpoints) to PATH. -.TP -\-v, \-\-version -Show version - -.SH COMMANDS -.TP -backup | \-b | \-\-backup -Take a snapshot now. -.TP -backup\-job | \-\-backup\-job -Take a snapshot (if needed) depending on schedule rules (used for cron jobs). -Back In Time will run in background for this. -.TP -benchmark-cipher | \-\-benchmark-cipher [FILE-SIZE] -Show a benchmark of all ciphers for ssh transfer. -.TP -check-config -Verify the profile in config, create snapshot path and crontab entries. -.TP -decode | \-\-decode [PATH] -Decode encrypted PATH. If no PATH is given Back In Time will read paths from -standard input. -.TP -last\-snapshot | \-\-last\-snapshot -Display last snapshot ID (if any) -.TP -last\-snapshot\-path | \-\-last\-snapshot\-path -Display the path to the last snapshot (if any) -.TP -pw\-cache | \-\-pw\-cache [start|stop|restart|reload|status] -Control the Password Cache Daemon. If no argument is given the Password Cache -will start in foreground. -.TP -remove[\-and\-do\-not\-ask\-again] | \-\-remove[\-and\-do\-not\-ask\-again] [SNAPSHOT_ID] -Remove the snapshot. If SNAPSHOT_ID is missing it will be prompted. SNAPSHOT_ID -can be an index (starting with 0 for the last snapshot) or the exact SnapshotID -(19 caracters like '20130606-230501-984'). -\fIremove\-and\-do\-not\-ask\-again\fR will remove the snapshot immediately. -Be careful with this! -.TP -restore | \-\-restore [WHAT [WHERE [SNAPSHOT_ID]]] -Restore file WHAT to path WHERE from snapshot SNAPSHOT_ID. If arguments are -missing they will be prompted. To restore to the original path WHERE can be an -empty string '' or just press Enter at the prompt. SNAPSHOT_ID can be an index -(starting with 0 for the last snapshot) or the exact SnapshotID -(19 caracters like '20130606-230501-984') -.TP -shutdown -Shutdown the computer after the snapshot is done. -.TP -smart\-remove -Remove snapshots based on the configured Smart-Remove pattern. -.TP -snapshots\-list | \-\-snapshots\-list -Display the list of snapshot IDs (if any) -.TP -snapshots\-list\-path | \-\-snapshots\-list\-path -Display the paths to snapshots (if any) -.TP -snapshots\-path | \-\-snapshots\-path -Display path where is saves the snapshots (if configured) -.TP -unmount | \-\-unmount -Unmount the profile. - -.SH A NOTE ON SECURITY -There was a paid security audit for EncFS in Feb 2014 which revealed several -potential vulnerabilities. -.TP -From https://defuse.ca/audits/encfs.htm -EncFS is probably safe as long as the adversary only gets one copy of -the ciphertext and nothing more. EncFS is not safe if the adversary -has the opportunity to see two or more snapshots of the ciphertext at -different times. EncFS attempts to protect files from malicious -modification, but there are serious problems with this feature. -.PP -This might be a problem with Back In Time snapshots. -.SH SEE ALSO -backintime-qt, backintime-config. -.PP -Back In Time also has a website: https://github.com/bit-team/backintime -.SH AUTHOR -This manual page was written by BIT Team(). diff --git a/common/mount.py b/common/mount.py index be99e3317..c16f4facf 100644 --- a/common/mount.py +++ b/common/mount.py @@ -1,35 +1,117 @@ -# Copyright (C) 2012-2021 Germar Reitze, Taylor Raack +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Taylor Raack # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""The mount API. + + The high-level mount API is :py:class:`Mount` and handles mount, + umount, remount and checks for *Back In Time*. The low-level mount API + is :py:class:`MountControl`. The latter can be used to create own + mounting services via subclassing it. See the following example. + + Example: + + See this template to create your own mounting service by inheriting + from :py:class:`MountControl`. All you need to do is: + + - Add your settings in ``qt/settingsdialog.py``. + - Add settings in ``common/config.py``. + - Use the following template class ``MountDummy``, rename and modify + it to your needs. + - Please use ``self.currentMountpoint`` as your local mountpoint. + - Your class should inherit from :py:class:`mount.MountControl`. + + As real usage example see the two classes :py:class:`sshtools.SSH` and + :py:class:`encfstools.EncFS_mount`. + + This is the template: :: + + class MountDummy(mount.MountControl): + def __init__(self, *args, **kwargs): + super(MountDummy, self).__init__(*args, **kwargs) + + self.all_kwargs = {} + + # First we need to map the settings. + # If is in kwargs (e.g. if this class is called with + # dummytools.Dummy( = ) this will map self. to + # kwargs[]; else self. = from config + # e.g. self.setattrKwargs(, , **kwargs) + self.setattrKwargs( + 'user', self.config.get_dummy_user(self.profile_id), **kwargs) + self.setattrKwargs( + 'host', self.config.get_dummy_host(self.profile_id), **kwargs) + self.setattrKwargs( + 'port', self.config.get_dummy_port(self.profile_id), **kwargs) + self.setattrKwargs( + 'password', + self.config.password(self.parent, self.profile_id), + store = False, **kwargs) + + self.setDefaultArgs() + + # If self.currentMountpoint is not the remote snapshot path + # you can specify a subfolder of self.currentMountpoint for + # the symlink + self.symlink_subfolder = None + + self.mountproc = 'dummy' + self.log_command = '%s: %s@%s' % (self.mode, self.user, self.host) + + def _mount(self): + # Mount the service + # Implement your mountprocess here. + pass + + def _umount(self): + # Umount the service + # Implement your unmountprocess here. + pass + + def preMountCheck(self, first_run = False): + # Check what ever conditions must be given for the mount to be + # done successful. + # Raise MountException('Error description') if service can not mount + # return True if everything is okay + # all pre|post_[u]mount_check can also be used to prepare + # things or clean up + return True + + def postMountCheck(self): + # Check if mount was successful + # Raise MountException('Error description') if not + return True + + def preUmountCheck(self): + # Check if service is safe to umount + # Raise MountException('Error description') if not + return True + + def postUmountCheck(self): + # Check if umount successful + # Raise MountException('Error description') if not + return True +""" +import getpass +import json import os import subprocess -import json -import gettext -from zlib import crc32 from time import sleep - +from zlib import crc32 +from pathlib import Path import config import logger -import tools import password -from exceptions import MountException, HashCollision +import tools +from exceptions import HashCollision, MountException -_=gettext.gettext -class Mount(object): +class Mount: """ This is the high-level mount API. This will handle mount, umount, remount and checks on the low-level :py:class:`MountControl` subclass backends for @@ -43,25 +125,23 @@ class Mount(object): running this will try to start it. Args: - cfg (config.Config): current config - profile_id (str): profile ID that should be used - tmp_mount (bool): if ``True`` mount to a temporary destination - parent (QWidget): parent widget for QDialogs or ``None`` if there - is no parent + cfg (config.Config): Current config. + profile_id (str): Profile ID to be used. + tmp_mount (bool): If ``True`` mount to a temporary destination. + parent (QWidget): Parent widget for QDialogs or ``None`` if there + is no parent. + + Dev note (buhtz, 2026-01): Rename that class into something like + MountOrchestrator or MountManager. """ - def __init__(self, - cfg = None, - profile_id = None, - tmp_mount = False, - parent = None): - self.config = cfg - if self.config is None: - self.config = config.Config() - - self.profile_id = profile_id - if self.profile_id is None: - self.profile_id = self.config.currentProfile() + def __init__(self, + cfg=None, + profile_id=None, + tmp_mount=False, + parent=None): + self.config = cfg or config.Config() + self.profile_id = profile_id or self.config.currentProfile() self.tmp_mount = tmp_mount self.parent = parent @@ -69,115 +149,132 @@ def __init__(self, cache = password.Password_Cache(self.config) action = None running = cache.status() + if not running: logger.debug('pw-cache is not running', self) action = 'start' + if running and not cache.checkVersion(): logger.debug('pw-cache is running but is an old version', self) action = 'restart' + bit = tools.which('backintime') + if not action is None and not bit is None and len(bit): cmd = [bit, 'pw-cache', action] - logger.debug('Call command: %s' - %' '.join(cmd), self) - proc = subprocess.Popen(cmd, - stdout = subprocess.DEVNULL, - stderr = subprocess.DEVNULL) + logger.debug(f'Call command: {cmd}', self) + + proc = subprocess.Popen( + cmd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + if proc.returncode: - logger.error('Failed to %s pw-cache: %s' - %(action, proc.returncode), - self) - pass + logger.error( + f'Failed to {action} pw-cache: {proc.returncode}', + self) - def mount(self, mode = None, check = True, **kwargs): - """ - High-level `mount`. Check if the selected ``mode`` need to be mounted, + def get_backend(self, mode, **kwargs): + mounttools = self.config.SNAPSHOT_MODES[mode][0] + return mounttools( + cfg=self.config, + profile_id=self.profile_id, + tmp_mount=self.tmp_mount, + mode=mode, + parent=self.parent, + **kwargs) + + def mount(self, mode=None, check=True, **kwargs): + """High-level `mount`. Check if the selected ``mode`` need to be mounted, select the low-level backend and mount it. Args: - mode (str): mode to use. One of 'local', 'ssh', 'local_encfs' or - 'ssh_encfs' - check (bool): if ``True`` run - :py:func:`MountControl.preMountCheck` before - mounting - **kwargs: keyword arguments paste to low-level - :py:class:`MountControl` subclass backend + mode (str): Mode to use. One of 'local', 'ssh', 'local_encfs' or + 'ssh_encfs'. + check (bool): If ``True`` run :py:func:`MountControl.preMountCheck` + before mounting. + **kwargs: Keyword arguments paste to low-level + :py:class:`MountControl` subclass backend. Returns: - str: Hash ID used as mountpoint + str: Hash ID used as mountpoint. Raises: - exceptions.MountException: - if a check failed - exceptions.HashCollision: - if Hash ID was used before but umount info wasn't - identical + exceptions.MountException: If a check failed. + exceptions.HashCollision: If hash ID was used before but umount + info wasn't identical. """ - self.config.PLUGIN_MANAGER.load(cfg = self.config) + self.config.PLUGIN_MANAGER.load(cfg=self.config) self.config.PLUGIN_MANAGER.mount(self.profile_id) + if mode is None: mode = self.config.snapshotsMode(self.profile_id) if self.config.SNAPSHOT_MODES[mode][0] is None: - #mode doesn't need to mount + # mode doesn't need to mount return 'local' + else: - while True: + + while True: # ??? + try: - mounttools = self.config.SNAPSHOT_MODES[mode][0] - backend = mounttools(cfg = self.config, - profile_id = self.profile_id, - tmp_mount = self.tmp_mount, - mode = mode, - parent = self.parent, - **kwargs) - return backend.mount(check = check) + backend = self.get_backend(mode, **kwargs) + return backend.mount(check=check) + except HashCollision as ex: logger.warning(str(ex), self) del backend check = False + continue + break - def umount(self, hash_id = None): - """ - High-level `unmount`. Unmount the low-level backend. This will read + def umount(self, hash_id=None): + """High-level `unmount`. Unmount the low-level backend. This will read unmount infos written next to the mountpoint identified by ``hash_id`` and unmount it. Args: hash_id (bool): Hash ID used as mountpoint before that should get - unmounted + unmounted. Raises: - exceptions.MountException: - if a check failed + exceptions.MountException: If a check failed. """ - self.config.PLUGIN_MANAGER.load(cfg = self.config) + self.config.PLUGIN_MANAGER.load(cfg=self.config) self.config.PLUGIN_MANAGER.unmount(self.profile_id) + if hash_id is None: hash_id = self.config.current_hash_id + if hash_id == 'local': - #mode doesn't need to umount + # mode doesn't need to umount return - else: - umount_info = os.path.join(self.config._LOCAL_MOUNT_ROOT, hash_id, 'umount') - with open(umount_info, 'r') as f: - data_string = f.read() - f.close() - kwargs = json.loads(data_string) - mode = kwargs.pop('mode') - mounttools = self.config.SNAPSHOT_MODES[mode][0] - backend = mounttools(cfg = self.config, - profile_id = self.profile_id, - tmp_mount = self.tmp_mount, - mode = mode, - hash_id = hash_id, - parent = self.parent, - **kwargs) - backend.umount() - - def preMountCheck(self, mode = None, first_run = False, **kwargs): + + umount_info = os.path.join( + self.config._LOCAL_MOUNT_ROOT, hash_id, 'umount') + + with open(umount_info, 'r') as f: + data_string = f.read() + f.close() + + kwargs = json.loads(data_string) + mode = kwargs.pop('mode') + mounttools = self.config.SNAPSHOT_MODES[mode][0] + backend = mounttools(cfg=self.config, + profile_id=self.profile_id, + tmp_mount=self.tmp_mount, + mode=mode, + hash_id=hash_id, + parent=self.parent, + **kwargs) + + backend.umount() + + def preMountCheck(self, mode=None, first_run=False, **kwargs): """ High-level check. Run :py:func:`MountControl.preMountCheck` to check if all conditions for :py:func:`Mount.mount` are set. @@ -186,38 +283,40 @@ def preMountCheck(self, mode = None, first_run = False, **kwargs): correct before saving them. Args: - mode (str): mode to use. One of 'local', 'ssh', - 'local_encfs' or 'ssh_encfs' - first_run (bool): run intense checks that only need to run after - changing settings but not every time before - mounting - **kwargs: keyword arguments paste to low-level - :py:class:`MountControl` subclass backend + mode (str): Mode to use. One of 'local', 'ssh', 'local_encfs' or + 'ssh_encfs'. + first_run (bool): Run intense checks that only need to run after + changing settings but not every time before mounting. + **kwargs: Keyword arguments paste to low-level + :py:class:`MountControl` subclass backend. Returns: - bool: ``True`` if all checks where okay + bool: ``True`` if all checks where okay. Raises: - exceptions.MountException: - if a check failed + exceptions.MountException: If a check failed. """ if mode is None: mode = self.config.snapshotsMode(self.profile_id) - if self.config.SNAPSHOT_MODES[mode][0] is None: - #mode doesn't need to mount + # sshtools.SSH, encfstools.EncFS_mount, encfstools.EncFS_SSH + Mounttools = self.config.SNAPSHOT_MODES[mode][0] + + # "local" mode + if Mounttools is None: + # mode doesn't need to mount return True - else: - mounttools = self.config.SNAPSHOT_MODES[mode][0] - backend = mounttools(cfg = self.config, - profile_id = self.profile_id, - tmp_mount = self.tmp_mount, - mode = mode, - parent = self.parent, - **kwargs) - return backend.preMountCheck(first_run) - def remount(self, new_profile_id, mode = None, hash_id = None, **kwargs): + backend = Mounttools(cfg=self.config, + profile_id=self.profile_id, + tmp_mount=self.tmp_mount, + mode=mode, + parent=self.parent, + **kwargs) + + return backend.preMountCheck(first_run) + + def remount(self, new_profile_id, mode=None, hash_id=None, **kwargs): """ High-level `remount`. Unmount the old profile presented by ``hash_id`` and mount new profile ``new_profile_id`` with mode ``mode``. If old and @@ -253,59 +352,92 @@ def remount(self, new_profile_id, mode = None, hash_id = None, **kwargs): """ if mode is None: mode = self.config.snapshotsMode(new_profile_id) + if hash_id is None: hash_id = self.config.current_hash_id if self.config.SNAPSHOT_MODES[mode][0] is None: - #new profile don't need to mount. - self.umount(hash_id = hash_id) + # new profile don't need to mount. + self.umount(hash_id=hash_id) return 'local' if hash_id == 'local': - #old profile don't need to umount. + # old profile don't need to umount. self.profile_id = new_profile_id - return self.mount(mode = mode, **kwargs) + return self.mount(mode=mode, **kwargs) mounttools = self.config.SNAPSHOT_MODES[mode][0] - backend = mounttools(cfg = self.config, - profile_id = new_profile_id, - tmp_mount = self.tmp_mount, - mode = mode, - parent = self.parent, - **kwargs) + backend = mounttools( + cfg=self.config, + profile_id=new_profile_id, + tmp_mount=self.tmp_mount, + mode=mode, + parent=self.parent, + **kwargs + ) + if backend.compareRemount(hash_id): - #profiles uses the same settings. just swap the symlinks - backend.removeSymlink(profile_id = self.profile_id) - backend.setSymlink(profile_id = new_profile_id, hash_id = hash_id) + # profiles uses the same settings. just swap the symlinks + backend.removeSymlink(profile_id=self.profile_id) + backend.setSymlink(profile_id=new_profile_id, hash_id=hash_id) return hash_id else: - #profiles are different. we need to umount and mount again - self.umount(hash_id = hash_id) + # profiles are different. we need to umount and mount again + self.umount(hash_id=hash_id) self.profile_id = new_profile_id - return self.mount(mode = mode, **kwargs) + return self.mount(mode=mode, **kwargs) -class MountControl(object): - """ - This is the low-level mount API. This should be subclassed by backends. + def init_backend(self, mode=None, **kwargs): + """ + High-level init. Run :py:func:`MountControl.init_backend` to initiate + the backend if not configured yet. + + Args: + mode (str): mode to use. One of 'local', 'ssh', + 'local_encfs' or 'ssh_encfs' + **kwargs: keyword arguments paste to low-level + :py:class:`MountControl` subclass backend + + Raises: + exceptions.MountException: if init_backend failed + """ + if mode is None: + mode = self.config.snapshotsMode(self.profile_id) + + if self.config.SNAPSHOT_MODES[mode][0] is None: + # mode doesn't need to mount + return True + + backend = self.get_backend(mode, **kwargs) + + return backend.init_backend() + + +class MountControl: + """This is the low-level mount API. This should be subclassed by backends. Subclasses should have its own ``__init__`` but **must** also call the - inherited ``__init__``. + inherited ``__init__``. See module description (:py:mod:`mount`) for + a detailed example. + + You **must** overwrite methods: + + - :py:func:`MountControl._mount` + + You **can** overwrite methods: - You **must** overwrite methods:\n - :py:func:`MountControl._mount` + - :py:func:`MountControl._umount` + - :py:func:`MountControl.preMountCheck` + - :py:func:`MountControl.postMountCheck` + - :py:func:`MountControl.preUmountCheck` + - :py:func:`MountControl.postUmountCheck` - You **can** overwrite methods:\n - :py:func:`MountControl._umount`\n - :py:func:`MountControl.preMountCheck`\n - :py:func:`MountControl.postMountCheck`\n - :py:func:`MountControl.preUmountCheck`\n - :py:func:`MountControl.postUmountCheck` + These arguments (all of type :py:obj:`str`) **must** be defined in + ``self`` namespace by subclassing ``__init__`` method: - These arguments **must** be defined in ``self`` namespace by - subclassing ``__init__`` method:\n - mountproc (str): process used to mount\n - log_command (str): shortened form of mount command used in logs\n - symlink_subfolder (str):mountpoint-subfolder which should be linked\n + - ``mountproc``: process used to mount + - ``log_command``: shortened form of mount command used in logs + - ``symlink_subfolder``: mountpoint-subfolder which should be linked Args: cfg (config.Config): current config @@ -319,26 +451,30 @@ class MountControl(object): ``ssh_encfs`` hash_collision (int): global value used to prevent hash collisions on mountpoints - """ - CHECK_FUSE_GROUP = False + Dev note (buhtz, 2026-01): Rename that class into something like + MountController or MountBase + """ def __init__(self, - cfg = None, - profile_id = None, - hash_id = None, - tmp_mount = False, - parent = None, - symlink = True, + cfg=None, + profile_id=None, + hash_id=None, + tmp_mount=False, + parent=None, + symlink=True, *args, **kwargs): - self.config = cfg - if self.config is None: - self.config = config.Config() + # The following members should get valid values from the inheriting + # class. + self.mode = None + self.log_command = None + self.mountproc = None + self.symlink_subfolder = None + + self.config = cfg or config.Config() - self.profile_id = profile_id - if self.profile_id is None: - self.profile_id = self.config.currentProfile() + self.profile_id = profile_id or self.config.currentProfile() self.tmp_mount = tmp_mount self.hash_id = hash_id @@ -346,13 +482,15 @@ def __init__(self, self.symlink = symlink self.local_host = self.config.host() - self.local_user = self.config.user() - self.pid = self.config.pid() + self.local_user = getpass.getuser() + self.pid = str(os.getpid()) self.all_kwargs = {} - self.setattrKwargs('mode', self.config.snapshotsMode(self.profile_id), **kwargs) - self.setattrKwargs('hash_collision', self.config.hashCollision(), **kwargs) + self.setattrKwargs( + 'mode', self.config.snapshotsMode(self.profile_id), **kwargs) + self.setattrKwargs( + 'hash_collision', self.config.hashCollision(), **kwargs) def setDefaultArgs(self): """ @@ -360,76 +498,95 @@ def setDefaultArgs(self): ``self.all_kwargs`` need to be filled through :py:func:`setattrKwargs` before calling this. """ - #self.destination should contain all arguments that are nessesary for - #mount. + # self.destination should contain all arguments that are necessary for + # mount. args = list(self.all_kwargs.keys()) self.destination = '%s:' % self.all_kwargs['mode'] + logger.debug(f"{self.destination=}") + args.remove('mode') args.sort() + for arg in args: self.destination += ' %s' % self.all_kwargs[arg] - #unique id for every different mount settings. Similar settings even in - #different profiles will generate the same hash_id and so share the same - #mountpoint + # Unique id for every different mount settings. Similar settings even + # in different profiles will generate the same hash_id and so share + # the same mountpoint. if self.hash_id is None: self.hash_id = self.hash(self.destination) + logger.debug(f"{self.hash_id=}") + # e.g. ~/.local/share/backintime/mnt self.mount_root = self.config._LOCAL_MOUNT_ROOT - self.snapshots_path = self.config.snapshotsPath(profile_id = self.profile_id, - mode = self.mode, - tmp_mount = self.tmp_mount) + self.snapshots_path = self.config.snapshotsPath( + profile_id=self.profile_id, + mode=self.mode, + tmp_mount=self.tmp_mount) self.hash_id_path = self.hashIdPath() self.currentMountpoint = self.mountpoint() self.lock_path = self.lockPath() self.umount_info = self.umountInfoPath() - def mount(self, check = True): + def mount(self, check=True): """ - Low-level `mount`. Set mountprocess lock and prepair mount, run checks + Low-level `mount`. Set mountprocess lock and prepare mount, run checks and than call :py:func:`_mount` for the subclassed backend. Finally set mount lock and symlink and release mountprocess lock. Args: - check (bool): if ``True`` run :py:func:`preMountCheck` before - mounting + check (bool): If ``True`` run :py:func:`preMountCheck` before + mounting. Returns: - str: Hash ID used as mountpoint + str: Hash ID used as mountpoint. Raises: - exceptions.MountException: - if a check failed - exceptions.HashCollision: - if Hash ID was used before but umount info wasn't - identical + exceptions.MountException: If a check failed. + exceptions.HashCollision: If Hash ID was used before but umount + info wasn't identical. """ self.createMountStructure() self.mountProcessLockAcquire() + try: if self.mounted(): + if not self.compareUmountInfo(): - #We probably have a hash collision + # We probably have a hash collision self.config.incrementHashCollision() - raise HashCollision(_('Hash collision occurred in hash_id %s. Incrementing global value hash_collision and try again.') % self.hash_id) - logger.info('Mountpoint %s is already mounted' %self.currentMountpoint, self) + raise HashCollision( + f'Hash collision occurred in hash_id {self.hash_id}. ' + 'Incrementing global value hash_collision and ' + 'trying again.') + + logger.info('Mountpoint {} is already mounted' + .format(self.currentMountpoint), + self) else: if check: self.preMountCheck() + self._mount() self.postMountCheck() - logger.info('mount %s on %s' - %(self.log_command, self.currentMountpoint), - self) + + logger.info( + f'mount {self.log_command} on {self.currentMountpoint}', + self) self.writeUmountInfo() + except Exception: + # ??? raise + else: self.mountLockAquire() self.setSymlink() + finally: self.mountProcessLockRelease() + return self.hash_id def umount(self): @@ -442,30 +599,44 @@ def umount(self): exceptions.MountException: if a check failed """ self.mountProcessLockAcquire() + + msg_begin = f'Mountpoint {self.currentMountpoint} ' + try: if not os.path.isdir(self.hash_id_path): - logger.info('Mountpoint %s does not exist.' % self.currentMountpoint, self) + logger.info(msg_begin + 'does not exist.', self) + else: if not self.mounted(): - logger.info('Mountpoint %s is not mounted' % self.currentMountpoint, self) + logger.info(msg_begin + 'is not mounted.', self) + else: if self.mountLockCheck(): - logger.info('Mountpoint %s still in use. Keep mounted' % self.currentMountpoint, self) + logger.info(msg_begin + 'still in use. Keep mounted.', + self) + else: self.preUmountCheck() self._umount() self.postUmountCheck() + if os.listdir(self.currentMountpoint): - logger.warning('Mountpoint %s not empty after unmount' %self.currentMountpoint, self) + logger.warning( + msg_begin + 'not empty after unmount', self) + else: - logger.info('unmount %s from %s' - %(self.log_command, self.currentMountpoint), - self) + logger.info( + f'unmount {self.log_command} ' + f'from {self.currentMountpoint}', + self) + except Exception: raise + else: self.mountLockRelease() self.removeSymlink() + finally: self.mountProcessLockRelease() @@ -482,16 +653,18 @@ def _umount(self): overwritten by backends which subclasses :py:class:`MountControl`. Raises: - exceptions.MountException: if unmount failed + exceptions.MountException: If unmount failed. """ try: subprocess.check_call(['fusermount', '-u', self.currentMountpoint]) - except subprocess.CalledProcessError: - raise MountException(_('Can\'t unmount %(proc)s from %(mountpoint)s') - %{'proc': self.mountproc, - 'mountpoint': self.currentMountpoint}) - def preMountCheck(self, first_run = False): + except subprocess.CalledProcessError as exc: + raise MountException( + _('Unable to unmount {mountprocess} from {mountpoint}.') + .format(mountprocess=self.mountproc, + mountpoint=self.currentMountpoint)) from exc + + def preMountCheck(self, first_run=False): """ Check what ever conditions must be given for the mount to be done successful. This **can** be overwritten in backends which @@ -564,34 +737,21 @@ def postUmountCheck(self): def checkFuse(self): """ - Check if command in self.mountproc is installed and user is part of - group ``fuse``. + Check if command in ``self.mountproc`` is installed. Raises: - exceptions.MountException: if either command is not available or - user is not in group fuse + exceptions.MountException: If either command is not available. """ - logger.debug('Check fuse', self) + if not tools.checkCommand(self.mountproc): - logger.debug('%s is missing' %self.mountproc, self) - raise MountException(_('%(proc)s not found. Please install e.g. %(install_command)s') - %{'proc': self.mountproc, - 'install_command': "'apt-get install %s'" %self.mountproc}) - if self.CHECK_FUSE_GROUP: - user = self.config.user() - try: - fuse_grp_members = grp.getgrnam('fuse')[3] - except KeyError: - #group fuse doesn't exist. So most likely it isn't used by this distribution - logger.debug("Group fuse doesn't exist. Skip test", self) - return - if not user in fuse_grp_members: - logger.debug('User %s is not in group fuse' %user, self) - raise MountException(_('%(user)s is not member of group \'fuse\'.\n ' - 'Run \'sudo adduser %(user)s fuse\'. To apply ' - 'changes logout and login again.\nLook at ' - '\'man backintime\' for further instructions.') - % {'user': user}) + logger.debug(f'{self.mountproc} is missing', self) + + raise MountException( + _('{command} not found. Please install it ' + '(e.g. via "{installcommand}")').format( + command=self.mountproc, + installcommand=f"'apt-get install {self.mountproc}'") + ) def mounted(self): """ @@ -606,106 +766,111 @@ def mounted(self): """ if os.path.ismount(self.currentMountpoint): return True + else: try: if os.listdir(self.currentMountpoint): - raise MountException(_('mountpoint %s not empty.') % self.currentMountpoint) + raise MountException( + _('Mountpoint {mntpoint} not empty.').format( + mntpoint=self.currentMountpoint)) + except FileNotFoundError: pass + return False def createMountStructure(self): """ Create folders that are necessary for mounting. - Folder structure in ~/.local/share/backintime/mnt/ (self.mount_root):: - - |\ .lock <= mountprocess lock that will prevent - | different processes modifying - | mountpoints at one time - | - |\ / <= ``self.hash_id_path`` - | \ will be shared by all profiles with - | | the same mount settings - | | - | |\ mountpoint/<= ``self.currentMountpoint`` - | | real mountpoint - | | - | |\ umount <= ``self.umount_info`` - | | json file with all nessesary args - | | for unmount - | | - | \ locks/ <= ``self.lock_path`` - | for each process you have a - | ``.lock`` file - | - |\ _/ <= sym-link to the right path. return by - | config.snapshotsPath - | (can be ../mnt//mount_point - | for ssh or - | ../mnt/// for - | fusesmb ...) - | - \ tmp__/ <= sym-link for testing mountpoints in - settingsdialog + Folder structure in ``~/.local/share/backintime/mnt/`` + (``self.mount_root``):: + + . + ├── .lock <= mountprocess lock that will prevent + │ different processes modifying + │ mountpoints at one time + │ + ├── / <= ``self.hash_id_path`` will be + │ │ shared by all profiles with the + │ │ same mount settings + │ │ + │ ├── mountpoint/ <= ``self.currentMountpoint`` real + │ │ mountpoint + │ │ + │ ├── umount <= ``self.umount_info`` json file with + │ │ all necessary args for unmount + │ │ + │ └── locks/ <= ``self.lock_path`` for each process + │ you have a ``.lock`` file + │ + ├── _/ <= sym-link to the right path. return + │ by config.snapshotsPath (can be + │ ../mnt//mount_point for ssh + │ or ../mnt/// + │ for fusesmb ...) + │ + └── tmp__/ <= sym-link for testing mountpoints + in settingsdialog """ tools.mkdir(self.mount_root, 0o700) tools.mkdir(self.hash_id_path, 0o700) tools.mkdir(self.currentMountpoint, 0o700, False) tools.mkdir(self.lock_path, 0o700) - def mountProcessLockAcquire(self, timeout = 60): + def mountProcessLockAcquire(self, timeout=60): """ Create a short term lock only for blocking other processes changing mounts at the same time. Args: - timeout (int): wait ``timeout`` seconds before fail acquiring - the lock + timeout (int): Wait ``timeout`` seconds before fail acquiring + the lock. Raises: - exceptions.MountException: - if timed out + exceptions.MountException: If timed out. """ lock_path = self.mount_root lockSuffix = '.lock' + # e.g. ~/.local/share/backintime/mnt/123456.lock lock = os.path.join(lock_path, self.pid + lockSuffix) + + # Every second count = 0 while self.checkLocks(lock_path, lockSuffix): count += 1 + if count == timeout: - raise MountException(_('Mountprocess lock timeout')) + raise MountException('Mountprocess lock timeout') + sleep(1) - logger.debug('Acquire mountprocess lock %s' - %lock, self) + logger.debug(f'Acquire mountprocess lock {lock}', self) + with open(lock, 'w') as f: f.write(self.pid) def mountProcessLockRelease(self): - """ - Remove mountprocess lock. - """ + """Remove mountprocess lock.""" lock_path = self.mount_root lockSuffix = '.lock' lock = os.path.join(lock_path, self.pid + lockSuffix) - logger.debug('Release mountprocess lock %s' - %lock, self) + + logger.debug(f'Release mountprocess lock {lock}', self) + if os.path.exists(lock): os.remove(lock) def mountLockAquire(self): """ - Create a lock for a mountpoint to prevent unmounting as long as this - process is still running. + Create a lock file for a mountpoint to prevent unmounting as long as + this process is running. """ - if self.tmp_mount: - lockSuffix = '.tmp.lock' - else: - lockSuffix = '.lock' + lockSuffix = '.tmp.lock' if self.tmp_mount else '.lock' lock = os.path.join(self.lock_path, self.pid + lockSuffix) - logger.debug('Set mount lock %s' - %lock, self) + + logger.debug(f'Set mount lock {lock}', self) + with open(lock, 'w') as f: f.write(self.pid) @@ -723,53 +888,76 @@ def mountLockRelease(self): """ Remove mountpoint lock for this process. """ - if self.tmp_mount: - lockSuffix = '.tmp.lock' - else: - lockSuffix = '.lock' + lockSuffix = '.tmp.lock' if self.tmp_mount else '.lock' lock = os.path.join(self.lock_path, self.pid + lockSuffix) + if os.path.exists(lock): - logger.debug('Remove mount lock %s' - %lock, self) + logger.debug(f'Remove mount lock {lock}', self) os.remove(lock) - def checkLocks(self, path, lockSuffix): - """ - Check if there are active locks ending with ``lockSuffix`` in ``path``. - If the process owning the lock doesn't exist anymore this will remove - the lock. + def checkLocks(self, path, lock_suffix): + """Check existence of active and foreign locks. + + The lock owning process is specified by the PID contained in the + filename of the lock file used. Lock files of the current process are + ignored and ``False`` is returned if they share the same tmp-mount + state. If a lock exist but its process not the lock is removed and + ``False`` returned. In that latter case mount symlinks related to that + lock also removed. Args: - path (str): full path to lock directory - lockSuffix (str): last part of locks name + path (str): Full path to lock directory. + lock_suffix (str): Last part of locks name. Returns: - bool: ``True`` if there are active locks in ``path`` + bool: ``True`` if there are active locks in ``path``. + + Raises: + FileNotFoundError: If the path does not exists. + """ - for f in os.listdir(path): - if not f[-len(lockSuffix):] == lockSuffix: + if isinstance(path, str): + path = Path(path) + + for lock_fp in path.iterdir(): + + # Not a lock file? + if lock_fp.suffix != lock_suffix: + # next file continue - is_tmp = os.path.basename(f)[-len(lockSuffix)-len('.tmp'):-len(lockSuffix)] == '.tmp' + + # Secondary suffix is "tmp"? (e.g. "12345.tmp.lock") + is_tmp = lock_fp.with_suffix('').suffix == '.tmp' + + # Extract PID from lock file name + lock_pid = lock_fp.stem if is_tmp: - lock_pid = os.path.basename(f)[:-len('.tmp')-len(lockSuffix)] - else: - lock_pid = os.path.basename(f)[:-len(lockSuffix)] + lock_pid = lock_pid[:-4] # cut ".tmp" from the end + + # Ignore process's own lock files. if lock_pid == self.pid: + # ...with the same tmp-state. if is_tmp == self.tmp_mount: + # Dev note (buhtz, 2024-11-22): I have no idea what makes + # a mount temporary. continue + if tools.processAlive(int(lock_pid)): return True - else: - logger.debug('Remove old and invalid lock %s' - %f, self) - #clean up - os.remove(os.path.join(path, f)) - for symlink in os.listdir(self.mount_root): - if symlink.endswith('_%s' % lock_pid): - os.remove(os.path.join(self.mount_root, symlink)) + + logger.debug(f'Remove old and invalid lock {lock_fp}', self) + + # Clean up the lock file + lock_fp.unlink() + + # Clean up related symlinks + for symlink in Path(self.mount_root).iterdir(): + if symlink.name.endswith(f'_{lock_pid}'): + symlink.unlink() + return False - def setattrKwargs(self, arg, default, store = True, **kwargs): + def setattrKwargs(self, arg, default, store=True, **kwargs): """ Set attribute ``arg`` in local namespace (self.arg). Also collect all args in ``self.all_kwargs`` which will be hashed later and used as @@ -877,42 +1065,65 @@ def setSymlink(self, profile_id = None, hash_id = None, tmp_mount = None): """ if not self.symlink: return + if profile_id is None: profile_id = self.profile_id + if hash_id is None: hash_id = self.hash_id + if tmp_mount is None: tmp_mount = self.tmp_mount - dst = self.config.snapshotsPath(profile_id = profile_id, - mode = self.mode, - tmp_mount = tmp_mount) + + dst = self.config.snapshotsPath(profile_id=profile_id, + mode=self.mode, + tmp_mount=tmp_mount) mountpoint = self.mountpoint(hash_id) + if self.symlink_subfolder is None: src = mountpoint else: src = os.path.join(mountpoint, self.symlink_subfolder) + if os.path.exists(dst): os.remove(dst) + os.symlink(src, dst) - def removeSymlink(self, profile_id = None, tmp_mount = None): + def removeSymlink(self, profile_id=None, tmp_mount=None): """ - Remove symlink ``~/.local/share/backintime/mnt/_`` + Remove symlink ``~/.local/share/backintime/mnt/_``. Args: - profile_id (str): Profile ID for the symlink - tmp_mount (bool): Symlink is a temporary link for testing new - settings + profile_id (str): Profile ID for the symlink. + tmp_mount (bool): Symlink is a temporary link for testing new + settings. """ if not self.symlink: return + if profile_id is None: profile_id = self.profile_id + if tmp_mount is None: tmp_mount = self.tmp_mount - os.remove(self.config.snapshotsPath(profile_id = profile_id, - mode = self.mode, - tmp_mount = tmp_mount)) + + symlink_filename = self.config.snapshotsPath( + profile_id=profile_id, + mode=self.mode, + tmp_mount=tmp_mount) + + try: + os.remove(symlink_filename) + + except FileNotFoundError as exc: + logger.error( + f'Can not remove unexisting symlink "{symlink_filename}". ' + 'See issue #2296 for details.') + logger.debug(str(exc)) + + else: + logger.debug(f'Symlink removed: "{symlink_filename}"') def hash(self, s): """ @@ -924,7 +1135,7 @@ def hash(self, s): Returns: str: hash of string ``s`` """ - return('%X' % (crc32(s.encode()) & 0xFFFFFFFF)) + return '%X' % (crc32(s.encode()) & 0xFFFFFFFF) def hashIdPath(self, hash_id = None): """ diff --git a/common/password.py b/common/password.py index 9046c49ef..2beb8f975 100644 --- a/common/password.py +++ b/common/password.py @@ -1,40 +1,25 @@ -# Copyright (C) 2012-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import sys import os import time import atexit import signal -import subprocess -import gettext -import re -import errno - import config import configfile import tools +import daemon import password_ipc import logger from exceptions import Timeout -_=gettext.gettext - -class Password_Cache(tools.Daemon): +class Password_Cache(daemon.Daemon): """ Password_Cache get started on User login. It provides passwords for BIT cronjobs because keyring is not available when the User is not @@ -45,20 +30,28 @@ class Password_Cache(tools.Daemon): def __init__(self, cfg = None, *args, **kwargs): self.config = cfg + if self.config is None: self.config = config.Config() + cachePath = self.config.passwordCacheFolder() + if not tools.mkdir(cachePath, 0o700): msg = 'Failed to create secure Password_Cache folder' logger.error(msg, self) + raise PermissionError(msg) + pid = self.config.passwordCachePid() - super(Password_Cache, self).__init__(pid, umask = 0o077, *args, **kwargs) + + super(Password_Cache, self).__init__( + pid, umask = 0o077, *args, **kwargs) + self.dbKeyring = {} self.dbUsr = {} self.fifo = password_ipc.FIFO(self.config.passwordCacheFifo()) - self.keyringSupported = tools.keyringSupported() + self.keyringSupported = tools.KEYRING_SUPPORTED def run(self): """ @@ -77,47 +70,64 @@ def run(self): if not self.collectPasswords(): logger.debug('Nothing to cache. Quit.', self) sys.exit(0) + self.fifo.create() atexit.register(self.fifo.delfifo) signal.signal(signal.SIGHUP, self.reloadHandler) logger.debug('Start loop', self) + while True: try: request = self.fifo.read() request = request.split('\n')[0] task, value = request.split(':', 1) + if task == 'get_pw': key = value - if key in list(self.dbKeyring.keys()): + + if key in self.dbKeyring: answer = 'pw:' + self.dbKeyring[key] - elif key in list(self.dbUsr.keys()): + + elif key in self.dbUsr: answer = 'pw:' + self.dbUsr[key] + else: answer = 'none:' + self.fifo.write(answer, 5) + elif task == 'set_pw': key, value = value.split(':', 1) self.dbUsr[key] = value - except IOError as e: - logger.error('Error in writing answer to FIFO: %s' % str(e), self) + except IOError as exc: + logger.error(f'Error in writing answer to FIFO: {exc}', self) + except KeyboardInterrupt: logger.debug('Quit.', self) break + except Timeout: + # That exception is thrown by tools.Alarm.handle() if the + # timeout ends. That Alarm was set by FIFO.read(). logger.error('FIFO timeout', self) + except Exception as e: logger.error('ERROR: %s' % str(e), self) - def reloadHandler(self, signum, frame): + def reloadHandler(self, _signum, _frame): """ reload passwords during runtime. """ time.sleep(2) cfgPath = self.config._LOCAL_CONFIG_PATH - del(self.config) + + del self.config + self.config = config.Config(cfgPath) - del(self.dbKeyring) + + del self.dbKeyring + self.dbKeyring = {} self.collectPasswords() @@ -127,81 +137,120 @@ def collectPasswords(self): """ run_daemon = False profiles = self.config.profiles() + for profile_id in profiles: mode = self.config.snapshotsMode(profile_id) + for pw_id in (1, 2): + if self.config.modeNeedPassword(mode, pw_id): + if self.config.passwordUseCache(profile_id): run_daemon = True - if self.config.passwordSave(profile_id) and self.keyringSupported: - service_name = self.config.keyringServiceName(profile_id, mode, pw_id) - user_name = self.config.keyringUserName(profile_id) + if (self.config.passwordSave(profile_id) + and self.keyringSupported): + service_name = self.config.keyringServiceName( + profile_id, mode, pw_id) + user_name = self.config.keyringUserName(profile_id) password = tools.password(service_name, user_name) + if password is None: continue - self.dbKeyring['%s/%s' %(service_name, user_name)] = password + + self.dbKeyring[f'{service_name}/{user_name}'] \ + = password + return run_daemon def checkVersion(self): info = configfile.ConfigFile() info.load(self.config.passwordCacheInfo()) + if info.intValue('version') < self.PW_CACHE_VERSION: return False + return True - def cleanupHandler(self, signum, frame): + def cleanup_handler(self, signum, frame): self.fifo.delfifo() - super(Password_Cache, self).cleanupHandler(signum, frame) + super(Password_Cache, self).cleanup_handler(signum, frame) -class Password(object): - """ - provide passwords for BIT either from keyring, Password_Cache or - by asking User. + +class Password: + """Provide passwords for BIT either from keyring, Password_Cache or + by asking user. """ - def __init__(self, cfg = None): + + def __init__(self, cfg=None): self.config = cfg + if self.config is None: self.config = config.Config() + self.cache = Password_Cache(self.config) self.fifo = password_ipc.FIFO(self.config.passwordCacheFifo()) self.db = {} - self.keyringSupported = tools.keyringSupported() + self.keyringSupported = self.cache.keyringSupported - def password(self, parent, profile_id, mode, pw_id = 1, only_from_keyring = False): + def password(self, + parent, + profile_id, + mode, + pw_id=1, + only_from_keyring=False, + refresh=False): """ - based on profile settings return password from keyring, + Based on profile settings return password from keyring, Password_Cache or by asking User. """ if not self.config.modeNeedPassword(mode, pw_id): return '' + service_name = self.config.keyringServiceName(profile_id, mode, pw_id) + user_name = self.config.keyringUserName(profile_id) + try: - return self.db['%s/%s' %(service_name, user_name)] + return self.db['%s/%s' % (service_name, user_name)] + except KeyError: pass + password = '' - if self.config.passwordUseCache(profile_id) and not only_from_keyring: - #from cache + + if (self.config.passwordUseCache(profile_id) + and not only_from_keyring + and not refresh): + # From cache password = self.passwordFromCache(service_name, user_name) - if not password is None: + + if password is not None: self.setPasswordDb(service_name, user_name, password) + return password - if self.config.passwordSave(profile_id): - #from keyring + + if self.config.passwordSave(profile_id) and not refresh: + # From keyring password = self.passwordFromKeyring(service_name, user_name) - if not password is None: + + if password is not None: self.setPasswordDb(service_name, user_name, password) + return password - if not only_from_keyring: - #ask user and write to cache + + if refresh or not only_from_keyring: + # Ask user and write to cache password = self.passwordFromUser(parent, profile_id, mode, pw_id) + if self.config.passwordUseCache(profile_id): self.setPasswordCache(service_name, user_name, password) + self.setPasswordDb(service_name, user_name, password) + return password + return password def passwordFromKeyring(self, service_name, user_name): @@ -220,50 +269,81 @@ def passwordFromCache(self, service_name, user_name): """ get password from Password_Cache """ - if self.cache.status(): - self.cache.checkVersion() - self.fifo.write('get_pw:%s/%s' %(service_name, user_name), timeout = 5) - answer = self.fifo.read(timeout = 5) - mode, pw = answer.split(':', 1) - if mode == 'none': - return None - return pw - else: + if not self.cache.status(): return None - def passwordFromUser(self, parent, profile_id = None, mode = None, pw_id = 1, prompt = None): - """ - ask user for password. This does even work when run as cronjob - and user is logged in. + self.cache.checkVersion() + self.fifo.write(f'get_pw:{service_name}/{user_name}', timeout=5) + answer = self.fifo.read(timeout = 5) + mode, pw = answer.split(':', 1) + + return None if mode == 'none' else pw + + + def passwordFromUser(self, + parent, + profile_id=None, + mode=None, + pw_id=1, + prompt=None): + """Ask user for password. + + Use terminal input or a dialog box if X is available. This does even + work when run as cronjob and user is logged in. + + Args: + parent: Parent of the ``QMessageDialog``. + profile_id(str): Profile identifier. + mode(str): Mode identifier (e.g. SSH (encrypted)). + pwd_id(int): See :data:`config.SNAPSHOT_MODES` for details. + prompt(str): Alternative string used as prompt. + + Return: + str: The password. """ + # Default prompt if prompt is None: - prompt = _('Profile \'%(profile)s\': Enter password for %(mode)s: ') % {'profile': self.config.profileName(profile_id), 'mode': self.config.SNAPSHOT_MODES[mode][pw_id + 1]} + prompt = _('Enter password for {mode} profile "{profile}":') \ + .format( + profile=self.config.profileName(profile_id), + mode=self.config.SNAPSHOT_MODES[mode][pw_id+1]) - tools.registerBackintimePath('qt') + tools.register_backintime_path('qt') x_server = tools.checkXServer() import_successful = False + if x_server: try: import messagebox import_successful = True + except ImportError: pass if not import_successful or not x_server: import getpass + alarm = tools.Alarm() alarm.start(300) + try: - password = getpass.getpass(prompt) + password = getpass.getpass(prompt + ' ') alarm.stop() + except Timeout: password = '' + return password - password = messagebox.askPasswordDialog(parent, self.config.APP_NAME, - prompt = prompt, - timeout = 300) + # Use QDialog as graphical prompt + password = messagebox.ask_password_dialog( + parent=parent, + title=self.config.APP_NAME, + prompt=prompt, + language_code=self.config.language(), + timeout=300) + return password def setPasswordDb(self, service_name, user_name, password): @@ -278,7 +358,8 @@ def setPassword(self, password, profile_id, mode, pw_id): store password to keyring and Password_Cache """ if self.config.modeNeedPassword(mode, pw_id): - service_name = self.config.keyringServiceName(profile_id, mode, pw_id) + service_name = self.config.keyringServiceName( + profile_id, mode, pw_id) user_name = self.config.keyringUserName(profile_id) if self.config.passwordSave(profile_id): @@ -295,4 +376,5 @@ def setPasswordKeyring(self, service_name, user_name, password): def setPasswordCache(self, service_name, user_name, password): if self.cache.status(): self.cache.checkVersion() - self.fifo.write('set_pw:%s/%s:%s' %(service_name, user_name, password), timeout = 5) + self.fifo.write( + f'set_pw:{service_name}/{user_name}:{password}', timeout=5) diff --git a/common/password_ipc.py b/common/password_ipc.py index 15a8e6cd6..90490dd97 100644 --- a/common/password_ipc.py +++ b/common/password_ipc.py @@ -1,19 +1,10 @@ -# Copyright (C) 2012-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os import sys import stat @@ -24,88 +15,120 @@ import logger -class FIFO(object): - """ - interprocess-communication with named pipes + +class FIFO: + """Inter-process communication (IPC) with named pipes using the first-in, + first-out principle (FIFO). + + Params: + fifo (str): Name of the named pipe file. + alarm (tools.Alarm): To handle read/write timeouts. """ + def __init__(self, fname): self.fifo = fname self.alarm = tools.Alarm() def delfifo(self): - """ - remove FIFO - """ + """Remove named pipe file.""" try: os.remove(self.fifo) + + # TODO: Catch FileNotFoundError only except: pass def create(self): - """ - create the FIFO in a way that only the current user can access it. + """Create the named pipe file in a way that only the current user has + access to it. """ if os.path.exists(self.fifo): self.delfifo() + try: + # Permissions are "rw- --- ---" os.mkfifo(self.fifo, 0o600) + except OSError as e: - logger.error('Failed to create FIFO: %s' % str(e), self) + logger.error(f'Failed to create named pipe file. Error: {e}', self) sys.exit(1) - def read(self, timeout = 0): - """ - read from fifo untill timeout. If timeout is 0 it will wait forever - for input. + def read(self, timeout=0): + """Read from named pipe until timeout. If timeout is 0 it will wait + forever for input. """ - #sys.stdout.write('read fifo\n') + # sys.stdout.write('read fifo\n') if not self.isFifo(): + # TODO raise an Exception or write to stderr sys.exit(1) + self.alarm.start(timeout) - with open(self.fifo, 'r') as fifo: - ret = fifo.read() + + with open(self.fifo, 'r') as handle: + # Will wait until data is available, + # or an exception (e.g. exception.Timeout) is raised. + # The latter will happen when the timeout is finished. + ret = handle.read() + + # If the alarm timeout ends but read() received not data, a + # exception.Timeout will be raised at this point. + # The exception will be caught far away in + # password.py::Password_Cache.run(). + self.alarm.stop() + return ret - def write(self, string, timeout = 0): - """ - write to fifo untill timeout. If timeout is 0 it will wait forever - for an other process that will read this. + def write(self, string, timeout=0): + """Write to named pipe file until timeout. If timeout is 0 it will wait + forever for an other process that will read this. """ - #sys.stdout.write('write fifo\n') if not self.isFifo(): + # TODO raise an Exception or write to stderr sys.exit(1) + self.alarm.start(timeout) + with open(self.fifo, 'w') as fifo: fifo.write(string) + + # See FIFO.read() to learn about "hidden" handling of Timeout + # exception. + self.alarm.stop() def isFifo(self): - """ - make sure file is still a FIFO and has correct permissions - """ + """Make sure file is still a FIFO and has correct permissions.""" try: s = os.stat(self.fifo) + except OSError: return False + if not s.st_uid == os.getuid(): - logger.error('%s is not owned by user' % self.fifo, self) + logger.error(f'{self.fifo} is not owned by user', self) return False + mode = s.st_mode if not stat.S_ISFIFO(mode): - logger.error('%s is not a FIFO' % self.fifo, self) + logger.error(f'{self.fifo} is not a named pipe file (FIFO)', self) return False + forbidden_perm = stat.S_IXUSR + stat.S_IRWXG + stat.S_IRWXO if mode & forbidden_perm > 0: - logger.error('%s has wrong permissions' % self.fifo, self) + logger.error(f'{self.fifo} has wrong permissions', self) return False + return True + class TempPasswordThread(threading.Thread): + """Provide password through temp FIFO. + + In case BIT is not configured yet this provides a password through temp + FIFO to backintime-askpass. """ - in case BIT is not configured yet provide password through temp FIFO - to backintime-askpass. - """ + def __init__(self, string): super(TempPasswordThread, self).__init__() self.pw = string diff --git a/common/pluginmanager.py b/common/pluginmanager.py index 6621de2aa..be5addb06 100644 --- a/common/pluginmanager.py +++ b/common/pluginmanager.py @@ -1,77 +1,216 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os import sys import tools -tools.registerBackintimePath('common') -tools.registerBackintimePath('plugins') -tools.registerBackintimePath('common', 'plugins') -tools.registerBackintimePath('qt', 'plugins') +tools.register_backintime_path('common') +tools.register_backintime_path('plugins') +tools.register_backintime_path('common', 'plugins') +tools.register_backintime_path('qt', 'plugins') import logger from exceptions import StopException + class Plugin: - def __init__(self): - return + """ Interface methods to customize behavior for different backup steps + + Back In Time allows to inform plugins (implemented in Python + files) about different steps ("events") in the backup process. + Plugins may implement special behavior to predefined + "events" that are declared in this interface class + as methods. + + To implement a new plugin create a new + class that inherits from this one and implement all + methods. + + Plugins are loaded by calling :py:func:`PluginManager.load`. + """ def init(self, snapshots): return True def isGui(self): + """Indicates a GUI-related plugin + + The return value shall indicate if the plugin + is related to the Back In Time GUI. + Loaded GUI-related plugins are called before non-GUI-related + plugins by the PluginManager. + + Returns: + True if plugin is GUI-related, otherwise False + """ return False def processBegin(self): + """Called before a backup process is started. + + A new snapshot is only taken if required (as configured). + + Returns: + ``None`` (return value will be ignored anyhow) + """ return def processEnd(self): + """Called after a backup process has ended + + Returns: + ``None`` (return value will be ignored anyhow) + """ return def error(self, code, message): + """Indicates errors during the backup process + + Called to send errors in the backup process + (while taking a snapshot) to plugins. + + Args: + code: A Back In Time error code + + Known error codes: + + 1: No or no valid configuration + (check the configuration file) + 2: A backup process is already running. + Make sure that automatic and manual backups + do not run at once. + 3: Snapshots directory not found + (eg. when a removable drive is not mounted) + 4: The requested snapshot for "now" already exists. + ``message`` contains the SID (snapshot ID) then. + 5: Error while taking a snapshot. + ``message`` contains more information (as string). + 6: New snapshot taken but with errors. + ``message`` contains the SID (snapshot ID) then. + + message: The error message for the code + (mostly an empty string by default) + + Returns: + ``None`` (return value will be ignored anyhow) + """ return def newSnapshot(self, snapshot_id, snapshot_path): + """ Called when the backup process has taken a new snapshot. + + A new snapshot is only taken by the backup process + if required (as configured). + + Args: + snapshot_id: The id of the new snapshot + snapshot_path: The path to the new snapshot + Returns: + ``None`` (return value will be ignored anyhow) + """ return def message(self, profile_id, profile_name, level, message, timeout): - return + """Send snapshot-related messages to plugins. + + Args: + profile_id: Profile ID from configuration. + profile_name: Profile name from the configuration. + level: 0 = INFO, 1 = ERROR + message: Message text. + timeout: Requested timeout in seconds to process the message. + Not used at the moment. (default -1 means "no timeout") + """ def appStart(self): + """ Called when the GUI of Back In Time was started. + + Not called when only the CLI command was started without the GUI. + + Returns: + ``None`` (return value will be ignored anyhow) + """ return def appExit(self): + """ Called when the GUI of Back In Time is closed + + Returns: + ``None`` (return value will be ignored anyhow) + """ return def mount(self, profileID = None): + """ Called when mounting a filesystem for the profile may be necessary. + + Args: + profileID: Profile ID from the configuration + + Returns: + ``None`` (return value will be ignored anyhow) + """ return def unmount(self, profileID = None): + """ Called when unmounting a filesystem for a profile may be necessary + + Args: + profileID: Profile ID from the configuration + + Returns + ``None`` (return value will be ignored anyhow) + """ return + class PluginManager: + """ Central interface for loading plugins and calling their API + + Back In Time allows to inform plugins (implemented in Python + files) about different steps ("events") in the backup process. + + Use this class to load installed plugin classes + and call their methods (see the interface declared by + :py:class:`Plugin`). + + Plugins are loaded by calling :py:func:`PluginManager.load`. + + When you call a plugin function of the PluginManager it will + call this plugin function for all loaded plugins. + """ + # TODO 09/28/2022: Should inherit from + implement class "Plugin" + def __init__(self): self.plugins = [] self.hasGuiPlugins = False self.loaded = False - def load(self, snapshots = None, cfg = None, force = False): + def load(self, snapshots=None, cfg=None, force=False): + """Loads plugins + + Loads all plugins from python source code files that are stored + in one of these plugin sub folders in the installation + root folder: + + 'plugins', 'common/plugins', 'qt/plugins' + + Plugins must inherit from :py:class:`Plugin` otherwise they + are silently ignored. + + Args: + snapshots (snapshots.Snapshots): Snapshot info + cfg (config.Config): Current configuration + force (bool): ``True`` to enforce reloading all plugins (``False`` + does only load if not already done) + """ if self.loaded and not force: return @@ -83,35 +222,59 @@ def load(self, snapshots = None, cfg = None, force = False): self.plugins = [] self.hasGuiPlugins = False - loadedPlugins = [] + self.loadedPlugins = [] + + # TODO 09/28/2022: Move hard coded plugin folders to configuration for path in ('plugins', 'common/plugins', 'qt/plugins'): - fullPath = tools.backintimePath(path) - if os.path.isdir(fullPath): - logger.debug('Register plugin path %s' %fullPath, self) - tools.registerBackintimePath(path) - for f in os.listdir(fullPath): - if f not in loadedPlugins and f.endswith('.py') and not f.startswith('__'): - try: - module = __import__(f[: -3]) - module_dict = module.__dict__ - - for key, value in list(module_dict.items()): - if key.startswith('__'): - continue - - if type(value) is type: - if issubclass(value, Plugin): - plugin = value() - if plugin.init(snapshots): - logger.debug('Add plugin %s' %f, self) - if plugin.isGui(): - self.hasGuiPlugins = True - self.plugins.insert(0, plugin) - else: - self.plugins.append(plugin) - loadedPlugins.append(f) - except BaseException as e: - logger.error('Failed to load plugin %s: %s' %(f, str(e)), self) + fullPath = tools.as_backintime_path(path) + + if not os.path.isdir(fullPath): + continue + + logger.debug(f'Register plugin path {fullPath}', self) + tools.register_backintime_path(path) + + for f in os.listdir(fullPath): + + if f.startswith('__') or not f.lower().endswith('.py'): + logger.debug(f'Not a plugin file: {f}', self) + continue + + logger.debug(f'Try to load plugin from {f}', self) + self._load_plugin_from_file(f, snapshots) + + def _load_plugin_from_file(self, file_name: str, snapshots: list): + if file_name in self.loadedPlugins: + logger.debug(f'Plugin file still loaded: {file_name}', self) + return + + try: + module = __import__(file_name[: -3]) + module_dict = module.__dict__ + + for key, value in list(module_dict.items()): + if key.startswith('__'): + continue + + if type(value) is type: + # A plugin must implement this class via inheritance + if issubclass(value, Plugin): + plugin = value() + + if plugin.init(snapshots): + logger.debug(f'Add plugin {file_name}', self) + + if plugin.isGui(): + self.hasGuiPlugins = True + self.plugins.insert(0, plugin) + + else: + self.plugins.append(plugin) + + self.loadedPlugins.append(file_name) + + except BaseException as exc: + logger.critical(f'Failed to load plugin {file_name}: {exc=}', self) def processBegin(self): ret_val = True @@ -148,7 +311,8 @@ def newSnapshot(self, snapshot_id, snapshot_path): def message(self, profile_id, profile_name, level, message, timeout = -1): for plugin in self.plugins: try: - plugin.message(profile_id, profile_name, level, message, timeout) + plugin.message( + profile_id, profile_name, level, message, timeout) except BaseException as e: self.logError(plugin, e) diff --git a/common/plugins/usercallbackplugin.py b/common/plugins/usercallbackplugin.py index 4f6f40ed3..d25ba4605 100644 --- a/common/plugins/usercallbackplugin.py +++ b/common/plugins/usercallbackplugin.py @@ -1,21 +1,13 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . import os import pluginmanager import logger @@ -23,46 +15,96 @@ from subprocess import Popen, PIPE from exceptions import StopException -_=gettext.gettext +_ = gettext.gettext class UserCallbackPlugin(pluginmanager.Plugin): + """ Executes a script file at different backup steps to customize behavior + + Back In Time allows to inform plugins (implemented in Python + files) about different steps ("events") in the backup process + via the :py:class:`pluginmanager.PluginManager`. + + This plugin calls a user-defined script file ("user-callback"). By default + that file is located in the config folder `$XDG_CONFIG_HOME/backintime/` + or another folder if command line optioni `--config` is used. + + The user-callback script is called with up to five positional arguments: + + 1. The profile ID from the config file (1=Main Profile, ...) + 2. The profile name (from the config file) + 3. A numeric code to indicate the reason why Back In Time + calls the script (see the method implementation + for details about the numeric code) + 4. Error code (only if argument 3 has the value "4") + or snapshot ID (only if argument 3 has the value "3") + 5. Snapshot name (only if argument 3 has the value "3") + + For more details and script examples see: + https://github.com/bit-team/user-callback + + Notes: + The user-callback script file is normally implemented as + shell script but could theoretically be implemented in + any script language (declared via the hash bang "#!" + in the first line of the script file. + """ def __init__(self): + # Dev note (2025-11, buhtz): Aryoda wrote that line one year ago in + # commit 9618d03. Not sure if there really is a need for it. + # In context of my syslog sub-identfiers, I need to remove that. + # My assumption is that at this point the syslog.openlog() was called. + # All this problems will vanish if #2286 is completed. + # logger.openlog('USERCALLBACKUP') return def init(self, snapshots): self.config = snapshots.config self.script = self.config.takeSnapshotUserCallback() - if not os.path.exists(self.script): - return False - return True - def callback(self, *args, profileID = None): + return os.path.exists(self.script) + + # TODO 09/28/2022: This method should be private (_callback) + def callback(self, *args, profileID=None): if profileID is None: profileID = self.config.currentProfile() + profileName = self.config.profileName(profileID) cmd = [self.script, profileID, profileName] - cmd.extend([str(x) for x in args]) - logger.debug('Call user-callback: %s' %' '.join(cmd), self) - if self.config.userCallbackNoLogging(): - stdout, stderr = None, None - else: - stdout, stderr = PIPE, PIPE + cmd.extend(str(x) for x in args) + + logger.debug(f'Call user-callback: {" ".join(cmd)}', self) + + stdout, stderr = PIPE, PIPE + try: callback = Popen(cmd, - stdout = stdout, - stderr = stderr, - universal_newlines = True) + stdout=stdout, + stderr=stderr, + universal_newlines=True) output = callback.communicate() + + # Stdout if output[0]: - logger.info('user-callback returned \'%s\'' %output[0].strip('\n'), self) + logger.info("user-callback returned '" + + output[0].strip('\n') + "'", + self) + + # Stderr if output[1]: - logger.error('user-callback returned \'%s\'' %output[1].strip('\n'), self) + logger.error("user-callback returned '" + + output[1].strip('\n') + "'", + self) + if callback.returncode != 0: - logger.warning('user-callback returncode: %s' %callback.returncode, self) + logger.warning( + f'user-callback returncode: {callback.returncode}', self) raise StopException() + except OSError as e: - logger.error("Exception when trying to run user callback: %s" % e.strerror, self) + logger.error( + f'Exception when trying to run user callback: {e.strerror}', + self) def processBegin(self): self.callback('1') diff --git a/common/po/README.md b/common/po/README.md index 65ae81277..42a6252c9 100644 --- a/common/po/README.md +++ b/common/po/README.md @@ -1,4 +1,26 @@ -Translation for BackInTime is done in https://translations.launchpad.net/backintime + +# Overview +Translation for Back In Time is done at https://translate.codeberg.org/engage/backintime. + +Please do NOT change po-files in this folder as they will get overwritten with files +exported from translation platform. + +See [Maintenance documentation about translation and localization](../../doc/maintain/2_localization.md) for details. + + +# Translation credits from removed languages +Because of missing translators some languages were removed in the past. The +translators' credits are archived here: + +``` +Interlingua [ie]: + - OIS +``` diff --git a/common/po/ar.po b/common/po/ar.po index 57f028c7f..d52390d2d 100644 --- a/common/po/ar.po +++ b/common/po/ar.po @@ -1,435 +1,273 @@ -# Arabic translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Jad Madi (@jadmadi) +# SPDX-FileCopyrightText: © Maytham Alsudany # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:18+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Arabic \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-10-29 20:07+0000\n" +"Last-Translator: BishoyEhab \n" +"Language-Team: Arabic \n" +"Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= " -"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "فشل في حفظ الإعداد: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "فشل في جلب الإعداد: %s" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "الملف الشخصي \"%s\" موجود بالفعل" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "لا يُمكنك إزالة آخر ملف شخصي !" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "مُعطل" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "في كل اقلاع/اعادة اقلاع" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "كل ٥ دقائق" - -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "كل ١٠ دقائق" - -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "كل ٣٠ دقيقة" - -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "كل ساعة" - -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "كل ساعتان" - -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "كل ٤ ساعات" - -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "كل ٦ ساعات" - -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "كل ١٢ ساعات" - -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "ساعات مخصصة" - -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "كل يوم" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"X-Generator: Weblate 5.13.3\n" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"كل التراخيص المستخدمه في هذا المشروع موجودة في المجلد {dir_link}. لاستخراج " +"أي ترخيص أو حقوق نشر أي ملف بأستخدام بيانات SPDX الوصفية, ارجع إلى " +"{readme_link}." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "عند وصل وسيط تخزين (udev)" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "كل أسبوع" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "كل شهر" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "يوم(أيام)" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "أسبوع(أسابيع)" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "سنة(سنين)" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "تحذير" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "الملف الشخصي الرئيسي" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "شهور" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "تشفير محلي بصيغة EncFS" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " إختباري!" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (تشفير EncFS)" -#: ../../common/config.py:129 +#: common/config.py:237 msgid "Local" msgstr "محلي" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" - -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "مفتاح SSH خاص" - -#: ../../common/config.py:131 +#: common/config.py:240 msgid "Local encrypted" -msgstr "" +msgstr "مشفرة محليا" -#: ../../common/config.py:131 ../../common/config.py:132 +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 msgid "Encryption" msgstr "التعمية" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "تشفير SSH" - -#: ../../common/config.py:135 -msgid "Default" -msgstr "إفتراضيّ" - -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" - -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" - -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" - -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" - -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" - -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" - -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" - -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" - -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" - -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" - -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" - -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" - -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "الملف الشخصي الرئيسي" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "الملف الشخصي: \"%s\"" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "مُجلد اللقطات غير صالح !" - -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "يجب أن تُحدد على الأقل مُجلداً واحداً للنسخ الأحتياطي !" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "لا يُمكنك أن تُضَمْن مُجلد النسخ الإحتياطي !" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "مفتاح SSH الخاص" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "لا يُمكنك أن تُضَمْن مُجلد نسخ إحتياطي فرعي !" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "الملف الشخصي: \"{name}\"" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s ليس مُجلداً !" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "مُجلد النسخ الاحتياطي غير صالح." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "يجب إختيار مجلد واحد على الأقل للنسخ الاحتياطي." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"لا يستطيع الكتابة على %s\n" -"هل انت مُتأكد بأن لديك حق الكتابة ؟" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "مُجلد: {path}" -#: ../../common/config.py:402 -#, python-format +#: common/config.py:332 common/config.py:347 msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"لا يمكن تضمين هذا المجلد للنسخ الاحتياطي لأنه جزء من وجهة النسخ الاحتياطي " +"نفسها." -#: ../../common/config.py:407 -#, python-format +#: common/config.py:366 +#, fuzzy, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"يجب أن تكون القيمة ل\"إزالة أقدم نسخة احتياطية إذا كانت المساحة الحرة أقل " +"من\" ({val_one}) أقل من أو تساوي الحد ل\"تحذير اذا انخفضت مساحة التخزين عن\"" +" ({val_two})" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "نسخ الروابط (احصل علي محتوي العنوان الرمزي)" - -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "خيارات الخبراء" - -#: ../../common/config.py:413 -#, python-format +#: common/config.py:371 msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"لا يستطيع إيجاد crontab.\n" -"ِِِِِِِِِِهل أنت مُتأكد بأن cron مثبت فعلاً ؟\n" -"أن لم يكن كذلك فعليك بإظهار جميع نُسخ الإحتياطية التلّقائية." +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "جدول udev لا يعمل مع طريقة {mode}" -#: ../../common/config.py:1478 +#: common/config.py:1569 msgid "Failed to write new crontab." -msgstr "فشك كتابة crontab جديد" - -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "تعذر العثور على UUID لـ \"%s\"" - -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" +msgstr "فشل كتابة crontab جديد." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" - -#: ../../common/encfstools.py:136 +#: common/config.py:1577 +#, fuzzy msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"أأنشئ مجلدا مشفرا جديدا؟" - -#: ../../common/encfstools.py:137 +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"لا يعمل كرون (Cron) رغم توفر أمر crontab. لن يتم تشغيل مهام النسخ الاحتياطي " +"المجدولة. قد يكون كرون مثبتًا ولكنه غير مفعل. جرب الأمر \"systemctl enable " +"cron\" أو استشر قنوات الدعم لتوزيعة جنو / لينكس الخاصة بك." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "فشل في حفظ التكوين" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "فشل في جلب التكوين" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "الملف الشخصي \"{name}\" موجود أصلاً." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "لا يستطيع إزالة الملف الشخصي الآخر." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "لا يستطيع ركب '{command}'" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "لم يوجد التكوين للمجلد المشفر." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "هل تريد انشاء مجلد مشفر جديد؟" + +#: common/encfstools.py:204 msgid "Cancel" -msgstr "ألغِ" +msgstr "إلغاء" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "رجاء أكد كلمة السر" +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "يرجى إدخال كلمة مرور لـ \"{user}\"." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "الكلمتان مختلفتان" +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "كلمة السر ليس مطابق." -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" -"تحوي النسخة 1.7.2 من encfs على علة مع خيار --reverse، رجاء حدث encfs" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 +#: common/encfstools.py:659 common/snapshots.py:1128 msgid "Take snapshot" msgstr "خذ لقطة" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "لا يستطيع ركب '{command}'" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "لا يستطيع تفكيك {mountprocess} من {mountpoint}." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" -"%(user)s ليس عضوا في مجموعة 'fuse'.\n" -" شغل 'sudo adduser %(user)s fuse'. لتطبيق التغييرات سجّل الخروج ثم ادخل.\n" -"ألق نظرة على 'man backintime' لمزيد من التعليمات." - -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "نقطة الضم %s ليست فارغة" +"لم يتم العثور على الأمر \"{command}\". يرجى تثبيته (على سبيل المثال ، " +"باستخدام \"{installcommand}\")" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "نقطة التوصيل {mntpoint} ليست فارغة." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "ادخل كلمة المرور لملف التعريف لوضع {mode} يسمى \"{profile}\":" -#: ../../common/snapshotlog.py:62 +#: common/schedule.py:238 +#, fuzzy, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" +"لا يستطيع تثبيت قاعدة Udev ماف الشخصي {profile_id}. خدمة DBus " +"'{dbus_interface}' ليس كانت موفره" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "لا يستطيع العثور على UUID لـ{path}" + +#: common/snapshots.py:378 common/snapshots.py:656 msgid "FAILED" msgstr "فشل" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "إستعادة التراخيص:" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "إستعادة الأذونات" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 msgid "Done" msgstr "تم" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" - -#: ../../common/snapshots.py:669 +#: common/snapshots.py:749 msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" msgstr "" -"لا يستطيع إيجاد مُجلد اللقطات.\n" -"إذا كان في قرص قابل للإزالة من فضلك وَصْلّهُ." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "تأجيل النسخ الاحتياطي أثناء وجوده على البطارية" + +#: common/snapshots.py:931 qt/app.py:377 +#, fuzzy +msgid "Can't find backup directory." +msgstr "لم نتمكن من العثور على مجلد اللقطات." + +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "إن كان على وحدة تخزين خارجية، يرجى توصيلها." + +#: common/snapshots.py:938 +#, fuzzy, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." msgstr[0] "ينتظر %s ثانية." msgstr[1] "ينتظر %s ثواني." msgstr[2] "ينتظر %s ثانية." @@ -437,1233 +275,2398 @@ msgstr[3] "ينتظر %s ثواني." msgstr[4] "ينتظر %s ثانية." msgstr[5] "ينتظر %s ثواني." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "فشل بأخذ لقطة %s !!!" +#: common/snapshots.py:1005 +#, fuzzy, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "فشل بأخذ لقطة {snapshot_id}." -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "يُنْهيّ" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "الرجاء الانتظار. جاري الانتهاء…" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "لا يُمكن إنشاء مُجلد: %s" +#: common/snapshots.py:1163 +#, fuzzy +msgid "Can't create directory." +msgstr "لا يمكن إنشاء مجلد" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "أحفظ ملف التعديل ..." +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "يتم حفظ ملف التكوين…" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "أحفظ ترخيص ..." +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "يتم حفظ الأذونات…" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: common/snapshots.py:1377 +#, fuzzy, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "وجد بقايا اللقطة {snapshot_id} ويمكن الاستمرار فيه." -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: common/snapshots.py:1401 +#, fuzzy, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "يتم إزالة مجلد بقايا {snapshot_id} من العملية الاخيرة" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "لا يُمكن إزالة المُجلد" + +#: common/snapshots.py:1466 +msgid "Creating backup" msgstr "" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "لا يُمكن إزالة مُجلد: %s" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "نجاح" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "خذ لقطة" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "عملية النقل جزئية بسبب حدوث خطأ" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "عملية النقل جزئية بسبب اختفاء ملفات المصدر (انظر 'man rsync')" + +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "انتهت عملية 'rsync' مع رمز الخروج {exit_code}" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "اطلع على 'man rsync' للمزيد من التفاصيل" + +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" +"رموز الخروج السلبية لrsync هم ارقام إشارة, اطلع على 'kill -l' و'man kill'" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "تعذرت إعادة تسمية %(new_path)s إلى %(path)s" +#: common/snapshots.py:1566 +#, fuzzy +msgid "Nothing changed, no new backup necessary" +msgstr "لا شيء تغير, لا حاجة للقطة جديدة" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "إزالة ذكية" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "لا يمكن إعادة تسمية {new_path} إلى {path}." -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "أزل اللقطات القديمة" +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "تطبيق القواعد لإلزالة اللقطات القديمة" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "حاول أبقاء حد أدنى من المساحة فارغة" +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "تطبيق قواعد الإحتفاظ" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "حاول إبقاء حد أدنى من المساحة فارغة" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "بأخطاء" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "محاولة للحفاظ على الحد الأدنى من {perc} ضمن مساحة الـ inode حرة" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 msgid "Now" msgstr "الآن" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "تعذر ضم %s" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "غير ممكن تركيب {sshfs}" + +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "لم يتم العثور على ssh-agent. يرجى التأكد من أنه مثبت." -#: ../../common/sshtools.py:315 +#: common/sshtools.py:489 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" -"تعذر فتح مفتاح ssh الخاص. إما أن كلمة السر خاطئة أو أنها غير متوفرة لـcron." +"لم يمكن فتح مفتاح SSH الخاص. كلمة المرور خاطئة أو كلمة المرور غير متاحة لـ " +"cron." + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "المسار البعيد موجود لكن ليس مجلدا." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "المسار البعيد غير قابل للكتابة." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "المسار المتباعد ليس قابل للتنفيذ." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "لا يمكن إنشاء المسار البعيد." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "المضيف البعيد {host} لا يدعم {command}" + +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "التأكد من الأوامر على المضيف {host} أعاد بخطأ غير معروف" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "المضيف البعيد {host} لا يدعم الروابط الصلبة" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "نسخ مفتاح SSH العام \"{pubkey}\" إلى المضيف البعيد \"{host}\"." -#: ../../common/sshtools.py:345 -#, python-format +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "يرجى إدخال كلمة مرور لـ \"{user}\"." + +#: common/tools.py:382 +#, fuzzy, python-brace-format msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"نظام الملفات الوجهة لـ {path} مُنسق باستخدام FAT، والذي لا يدعم الروابط " +"الصعبة. يُرجى استخدام نظام ملفات لينكس أصلي." -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +#: common/tools.py:414 +#, fuzzy, python-brace-format +msgid "{path} is not a valid directory." +msgstr "المسار البعيد موجود لكن ليس مجلدا." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" msgstr "" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "لم يوجد %s في ssh_known_hosts." +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "" -#: ../../common/sshtools.py:468 -#, python-format +#: common/tools.py:471 +#, fuzzy, python-brace-format msgid "" -"Remote path exists but is not a directory:\n" -" %s" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"نظام الملفات الوجهة لـ {path} مُنسق باستخدام FAT، والذي لا يدعم الروابط " +"الصعبة. يُرجى استخدام نظام ملفات لينكس أصلي." -#: ../../common/sshtools.py:470 -#, python-format +#: common/tools.py:482 +#, fuzzy, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"نظام الملفات الوجهة لـ {path} هو مشاركة مُركبة عبر SMB. يُرجى التأكد من أن " +"خادم SMB البعيد يدعم الروابط الرمزية أو تفعيل {copyLinks} في " +"{expertOptions}." + +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "نسخ الروابط (فك الارتباط بالروابط الرمزية)" + +#: common/tools.py:487 +msgid "Expert Options" +msgstr "خيارات الخبراء" -#: ../../common/sshtools.py:472 -#, python-format +#: common/tools.py:491 +#, fuzzy, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"نظام الملفات الوجهة لـ {path} هو مشاركة مُركبة عبر sshfs. لا يدعم sshfs " +"الروابط الصلبة. يُرجى استخدام وضع \"SSH\" بدلاً من ذلك." -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: common/tools.py:525 +msgid "File creation failed in this directory:" msgstr "" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/aboutdlg.py:50 +#, fuzzy +msgid "About Back In Time" +msgstr "العودة بالزمن" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" msgstr "" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/aboutdlg.py:92 +#, fuzzy +msgid "Authors:" +msgstr "المطورون" + +#: qt/aboutdlg.py:96 +#, fuzzy +msgid "Translators:" +msgstr "الترجمات" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" +"Jad Madi (@jadmadi)\n" +"Maytham Alsudany " -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." msgstr "" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" +#: qt/aboutdlg.py:115 +msgid "this link" msgstr "" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" msgstr "" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "أستخدم تدقيق المجموع لِكشف التغيُّرات" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "" + +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" +#: qt/app.py:332 +#, fuzzy, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"يبدو أن تطبيق {app_name} يعمل لأول مرة حيث لم يتم العثور على أي إعدادات." -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" +#: qt/app.py:337 +#, fuzzy +msgid "" +"Import an existing configuration from a backup location or another computer?" +msgstr "هل ترغب باستيراد إعدادات موجودة (من مجلد احتياطي أو من حاسوب آخر)؟" + +#: qt/app.py:379 +msgid "Then press OK." msgstr "" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:483 +msgid "Create a backup" msgstr "" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "استخدم وقت التعديل والحجم لاكتشاف تغييرات الملفات." + +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "خذ لقطة (طريقة المجموعة الاختبارية)" + +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "أستخدم المجامع الاختبارية لكشف التغيُّرات في الملفات." + +#: qt/app.py:492 qt/qtsystrayicon.py:125 +#, fuzzy +msgid "Pause backup process" +msgstr "أوقف عملية التقاط اللقطة" + +#: qt/app.py:496 qt/qtsystrayicon.py:130 +#, fuzzy +msgid "Resume backup process" +msgstr "استئناف عملية أخذ اللقطة" + +#: qt/app.py:500 qt/qtsystrayicon.py:135 +#, fuzzy +msgid "Stop backup process" +msgstr "أوقف عملية أخذ اللقطة" + +#: qt/app.py:504 +#, fuzzy +msgid "Refresh backup list" msgstr "حدث قائمة اللقطات" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "اسم اللقطة" +#: qt/app.py:508 +msgid "Name backup" +msgstr "" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" +#: qt/app.py:512 +#, fuzzy +msgid "Remove backup" msgstr "أزل اللقطة" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "عرض سجل اللقطات" +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "عرض اخر سجل" + +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "يجب إختيار مجلد واحد على الأقل للنسخ الاحتياطي." -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" msgstr "عرض اخر سجل" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "إعدادات" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "" + +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "إدارة الملفات الشخصية…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "تحرير استدعاء المستخدم" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" -msgstr "أغلق" +msgstr "أطفئ النظام" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." msgstr "أطفئ النظام بعد اكتمال أخذ اللقطة." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "إعداد اللغة…" + +#: qt/app.py:540 msgid "Exit" -msgstr "أخرج" +msgstr "خروج" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "مُساعدة" +#: qt/app.py:550 +#, fuzzy +msgid "man page: Back In Time" +msgstr "العودة بالزمن" -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" msgstr "" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "موقع إلكتروني" +#: qt/app.py:555 +#, fuzzy +msgid "man page: Profiles config file" +msgstr "ملف إعدادات الملفات الشخصية" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "سجل التغييرات" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" + +#: qt/app.py:573 msgid "FAQ" msgstr "الأسئلة الأكثر شيوعاً" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "" + +#: qt/app.py:577 msgid "Ask a question" msgstr "إسأل سُؤالاً" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "بلّغ عن عِلة" -#: ../../qt/app.py:174 ../../qt/app.py:1439 +#: qt/app.py:584 +msgid "Translation" +msgstr "الترجمة" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "عرض رسالة المشاركة في الترجمة." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "الإنتقال الى تشفير EncFS" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "عرض الرسالة حول إزالة ميزة التشفير EncFS." + +#: qt/app.py:599 msgid "About" msgstr "عنْ" -#: ../../qt/app.py:204 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "إسترجع" + +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "إسترجع الملفات والمجلدات المختارة الى اماكنهم الاصلية." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "إسترجع إلى …" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "إسترجع الملفات والمجلدات المختارة إلى موقع جديد." + +#: qt/app.py:615 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "إسترجع الملفات والمجلدات الظاهرة وكل محتوياتها الى اماكنهم الاصلية." + +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "إسترجع الملفات والمجلدات الظاهرة وكل محتوياتها إلى موقع جديد." + +#: qt/app.py:624 msgid "Up" msgstr "أعلى" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" msgstr "أظهر الملفات المخفيّة" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "إسترجع" +#: qt/app.py:630 +#, fuzzy +msgid "Compare backups…" +msgstr "قارن اللقطات…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" msgstr "" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "إسترجع إلى ..." +#: qt/app.py:663 +#, fuzzy +msgid "Shows the message about this Release Candidate again." +msgstr "عرض الرسالة حول إزالة ميزة التشفير EncFS." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "العودة بالزمن" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "النسخ الاحتياطي" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." +#: qt/app.py:717 +msgid "&Restore" +msgstr "&استرجع" + +#: qt/app.py:723 +msgid "&Help" +msgstr "مُساعدة" + +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "لقطات" +#: qt/app.py:784 +msgid "Light icon" +msgstr "" -#: ../../qt/app.py:260 -msgid "Snapshot" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -#: ../../qt/app.py:271 -msgid "View" +#: qt/app.py:808 +msgid "Icons only" msgstr "" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "إختصارات" +#: qt/app.py:811 +msgid "Text only" +msgstr "" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +#: qt/app.py:814 +msgid "Text below icons" msgstr "" -#: ../../qt/app.py:398 -msgid "Add to Include" +#: qt/app.py:817 +msgid "Text beside icon" msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"هذه المجلد غير موجود\n" +"في اللقطة المختارة حاليا." -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"هذه المجلد غير موجود\n" +"في اللقطة المختارة حاليا." -#: ../../qt/app.py:492 +#: qt/app.py:995 +#, fuzzy msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" -"لا يُمكن إيجاد مُجلد اللقطات.\n" -"إذا كان على قرص قابل للإزالة من فضلك وَصْلّهُ ومن ثم أضغط موافق" +"إذا تم إغلاق هذه النافذة، فإن برنامج الاستعادة سيتوقف عن تشغيل النظام عند " +"انتهاء اللقطة." -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +#: qt/app.py:998 +msgid "Close the window anyway?" msgstr "" -#: ../../qt/app.py:667 ../../qt/app.py:719 +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "تم، لا حاجة إلى النسخ الاحتياطي" + +#: qt/app.py:1260 msgid "Working:" msgstr "يعمل:" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "تم، لا حاجة للنسخ الاحتياطي" +#: qt/app.py:1267 +msgid "Working" +msgstr "يعمل" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "خطأ:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "خطأ" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "أُرسل:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "السرعة:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" +msgstr "الوقت المتبقي:" + +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "النسخ الاحتياطي" + +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "استعادة {path}" + +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "استرجع {path} إلى …" + +# ignore-placeholder-compare +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"مرحبا\n" +"لقد استخدمت تطبيق العودة في الزمن باللغة العربية عدة مرات من قبل.\n" +"لقد تم ترجمة {perc} من برنامج العودة في الزمن إلى اللغة العربية. بغض النظر إلى خبرتك التقنية, بإمكانك المساهمة في الترجمة ومن ثم برنامج العودة في الزمن نفسه.\n" +"الرجاء زيارة موقع {translation_platform_url} إذا رغبت في المشاركة. للمزيد من الاستفسارات او المساعدة, الرجاء زيارة {back_in_time_project_website}.\n" +"نعتذر للمقاطعة, هذه الرسالة لن تعرض مرة أخرى. بإمكانك الإطلاع على هذه الرسالة في أي وقت عبر قائمة المساعدة.\n" +"فريق العودة في الزمن" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "منصة الترجمة" + +#: qt/app.py:1658 +msgid "Website" +msgstr "الموقع الإلكتروني" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "عام" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "ترجمتك" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "جذر" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Home" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "مجلدات النسخ الاحتياطي" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "" -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." msgstr "" -"هل أنت مُتأكد بأنك تُريد إزالة اللقطة:\n" -"%s" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +#: qt/app.py:1725 +msgid "Open an issue" msgstr "" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." msgstr "" -#: ../../qt/app.py:1038 +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" +#: qt/app.py:1841 +msgid "Proceed with the backup?" msgstr "" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1875 +#, fuzzy, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD} تحذير{BOLDEND}: حذف الملفات في جذر نظام الملفات قد يؤدي إلى تعطل " +"نظامك بالكامل." -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "هل أنت متأكد من إستعادة هذه الملفات:" +#: qt/app.py:2107 +#, fuzzy +msgid "Backup name" +msgstr "النسخ الاحتياطي" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "إعدادات اللغة ستُفعّل فقط بعد إعادة تشغيل برنامج العودة بالزمن." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" msgstr "" -#: ../../qt/app.py:1101 +#: qt/backintime-qt-root.desktop:23 msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../qt/app.py:1105 -msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "لا تُزيل اللقطات المُسماّة." + +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "أعرض مُحتوى القرص الحالي" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "سؤال" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "لقطة: %s" +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." +msgstr "" +"إنشاء نسخ احتياطية ملحقة بـ {suffix}\n" +"قبل الكتابة فوق العناصر المحلية أو إزالتها." -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "أعرض اللقطة المُعِدة في %s" +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, fuzzy, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" +msgstr "" +"سيتم إعادة تسمية الإصدارات الأحدث من الملفات باستخدام ملحق {suffix} قبل " +"الاستعادة. إذا لم تعد تحتاج إليها ، يمكنك إزالتها باستخدام الأمر التالي:" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "إسترجع '%s'" +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"استعادة العناصر الغير موجودة\n" +"أو التي هي أحدث من الموجودة في الوجهة فقط.\n" +"باستخدام خيار \"rsync --update\"." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "إسترجع '%s' إلى ..." +#: qt/confirmrestoredialog.py:133 +#, fuzzy +msgid "Remove newer elements in original directory." +msgstr "إزالة العناصر الأحدث في المجلد الأصلي." -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "المؤلفون" +#: qt/confirmrestoredialog.py:135 +#, fuzzy +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"استعادة الملفات أو المجلدات المحددة إلى الوجهة الأصلية مع حذف الملفات أو " +"المجلدات غير الموجودة في اللقطة. كن حذراً للغاية لأن هذه العملية ستؤدي إلى " +"حذف الملفات والمجلدات التي تم استبعادها أثناء أخذ اللقطة." + +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"هل تريد حقًا استعادة هذا العنصر إلى المجلد الجديد\n" +"{path}؟" +msgstr[1] "" +"هل أنت متأكد أنك ترغب في استرجاع عنصر واحد إلى المجلد الجديد\n" +"{path}؟" +msgstr[2] "" +"هل تريد حقًا استعادة هذان العنصران إلى المجلد الجديد\n" +"{path}؟" +msgstr[3] "" +"هل أنت متأكد أنك ترغب في استرجاع عناصر إلى المجلد الجديد\n" +"{path}؟" +msgstr[4] "" +"هل أنت متأكد أنك ترغب في استرجاع عنصرًا إلى المجلد الجديد\n" +"{path}؟" +msgstr[5] "" +"هل أنت متأكد أنك ترغب في استرجاع عنصرًا إلى المجلد الجديد\n" +"{path}؟" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "لا توجد عناصر لاسترجاعها؟" +msgstr[1] "هل أنت متأكد أنك ترغب في استرجاع عنصر واحد؟" +msgstr[2] "هل أنت متأكد أنك ترغب في استرجاع عنصرين؟" +msgstr[3] "هل أنت متأكد أنك ترغب في استرجاع {n} عناصر؟" +msgstr[4] "هل أنت متأكد أنك ترغب في استرجاع {n} عنصرًا؟" +msgstr[5] "هل أنت متأكد أنك ترغب في استرجاع {n} عنصرًا؟" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "الترجمات" +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "ضمْن ملفات ومُجلدات" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "الرخصة" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "اضف للتضمين" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "اضف للاستبعاد" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "إعداد اللغة" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "مترجم: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "الإعدادات الافتراضية للنظام" + +#: qt/languagedialog.py:142 +#, fuzzy +msgid "Use operating system's language." +msgstr "استخدم لغة نظام التشغيل." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "عرض السجل الأخير" + +#: qt/logviewdialog.py:63 msgid "Last Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +msgstr "عرض السجل الأخير" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" -msgstr "ملف شخصي:" +msgstr "الملف الشخصي:" + +#: qt/logviewdialog.py:86 +#, fuzzy +msgid "Backups:" +msgstr "النسخ الاحتياطي" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "مُرَشِح:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "كل" +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] خطأ، [I] معلومات، [C] تغيير" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "أخطاء" +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "فك تشفير المسارات" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "كل" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 msgid "Changes" msgstr "تغييرات" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "معلومات" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "أخطاء" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] خطأ، [I] معلومات، [C] غيّر" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "لا توجد معلومات" +msgstr[1] "توجد معلومات واحدة" +msgstr[2] "توجد معلومتان" +msgstr[3] "معلومات" +msgstr[4] "معلومات" +msgstr[5] "معلومات" + +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "فشل نقل rsync (تجريبي)" + +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "إدارة الملفات الشخصية" + +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "تعديل" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "أضف" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "أزل" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&عام" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&ضمْن" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&استثني" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" msgstr "" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&خيارات" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "خيارات الخبير" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "استعادة الإعدادات" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "ملف شخصي جديد" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "أعِد تسمية الملف الشخصي" + +#: qt/manageprofiles/__init__.py:215 +#, fuzzy, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "هل أنت متأكد أنك تريد حذف الملف الشخصي \"{name}\"؟" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "خطأ" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "يعمل..." +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "اليوم" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." +msgstr "" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "البارحة" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "هذا الأسبوع" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "الأسبوع الماضي" +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "أوقف عملية التقاط اللقطة" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "هذه ليست لقطة بلا عرض حي لملفاتك المحلية" +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "إستثني ملف" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "اعرض السجل كاملا" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "تعديل" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "أضف" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "أزل" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "عام" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "أين سيتم حفظ اللقطات" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "المجلد" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "إعدادات SSH" +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "مجلدات النسخ الاحتياطي" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "المُضيف:" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "المنفذ:" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "لا يُمكن إزالة المُجلد" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "مستخدم:" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "أظهر الملفات المخفيّة" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "مُتقدم" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" msgstr "" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "أوقف عملية أخذ اللقطة" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "إستثني مُجلد" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "الإفتراضي" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" -msgstr "جدول" +msgstr "جدول زمني" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "يوم:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" msgstr "يوم الأسبوع:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "ساعة:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "ساعات:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:93 +#, fuzzy +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"قم بتشغيل Back In Time بمجرد توصيل محرك الأقراص (مرة واحدة فقط كل X أيام).\n" +"سيتم مطالبتك بكلمة مرور sudo الخاصة بك." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"شغل \"Back In Time\" بشكل متكرر. هذا مفيد إذا لم يكن الكمبيوتر قيد التشغيل " +"بانتظام." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" msgstr "كل:" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "تفعيل تسجيل رسائل التصحيح" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "يكتب رسائل مستوى التصحيح في سجل النظام عبر \"--debug\"." + +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"تحذير: استخدم هذا مؤقتًا فقط للتشخيص، لأنه يولد كمية كبيرة من البيانات." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "ضمْن" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "مُعطل" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "ضمْن ملفات ومُجلدات" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "في كل إقلاع/إعادة إقلاع" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "كل {n} دقيقة" +msgstr[1] "كل {n} دقيقة" +msgstr[2] "كل {n} دقيقتان" +msgstr[3] "كل {n} دقائق" +msgstr[4] "كل {n} دقيقة" +msgstr[5] "كل {n} دقيقة" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "كل{n} ساعة" +msgstr[1] "كل {n} ساعة" +msgstr[2] "كل {n} ساعتين" +msgstr[3] "بضع {n} ساعات" +msgstr[4] "كل {n} ساعة" +msgstr[5] "كل{n} ساعة" + +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "ساعات مخصصة" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "أضف ملف" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "كل يوم" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "متكررة (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "عند وصل وسيط تخزين (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "كل أسبوع" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "كل شهر" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "كل سنة" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "ساعات" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "أضف مُجلد" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "يوم(أيام)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "أسبوع(أسابيع)" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "أستثني" +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "شهر(شهور)" -#: ../../qt/settingsdialog.py:434 +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"يمكن أن تكون الساعات المخصصة قائمة مفصولة بفواصل من الساعات فقط (مثل " +"8،12،18،23) أو */3 للنسخ الاحتياطية الدورية كل 3 ساعات." + +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/sshkeyselector.py:65 +#, fuzzy +msgid "Choose an existing private key file from somewhere else." msgstr "" +"Изберете съществуващ частен ключ (обикновено озаглавен \"id_rsa\" или " +"\"id_ed25519\" в по-нови системи)." -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +#, fuzzy +msgid "Create a new SSH key without passphrase." +msgstr "Неуспех при създаване на нов SSH ключ в {path}." -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, fuzzy, python-brace-format +msgid "Full path: {path}" +msgstr "Пълен път за моментни архивни копия:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "Частен ключ:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +#, fuzzy +msgid "Use system SSH configuration" +msgstr "Вмъкване на конфигурация" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Да не се премахват именувани архиви" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH Прокси" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Настройки" - -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Включване на известяването" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Хост:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Изключване на моментни архиви при работа на батерия" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Порт:" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Енергийното състояние не е налично от системата" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Потребител:" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Свържете се с отдалечената система чрез прокси (известен също като междинен " +"хост). Вижте \"-J\" в документацията на командата \"ssh\" или \"ProxyJump\" " +"в страницата с ръководство на \"ssh_config\" за подробности." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}ИНФОРМАЦИЯ{ENDBOLD}: В режим „SSH криптиране“ функционират само " +"единични или двойни звездички (напр. {example2}). Други типове заместващи " +"символи и шаблони ще бъдат игнорирани (напр. {example1}). Имената на " +"файловете са непредвидими в този режим поради криптиране от EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Изключване на пътища, файлове или директории" + +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Шаблон за изключване" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Добавяне на файл" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Продължаване при грешки (запазване на непълни моментни архиви)" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Добавяне на директории" + +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "Въпрос" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Ниво на записа:" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Изключете файлове, по-големи от:" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Нищо" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Изключете файлове, по-големи от: {size_unit}." -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Промени и грешки" +#: qt/manageprofiles/tab_exclude.py:140 +#, fuzzy +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Изключете файлове, по-големи от стойността в %(prefix)s. При изключен 'Full " +"rsync mode', това ще засегне само новите файлове, защото това е преносна " +"опция за rsync, а не изключваща такава. Така че големите файлове, които са " +"били архивирани преди, ще останат в моментните копия дори и да са били " +"променени." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Шаблон за изключване" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Пременяйте тези настройки само ако знаете какво правите !" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Шаблон за изключване" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" msgstr "" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Изключване на файл" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Изключване на директории" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" +"Деактивирано, защото този шаблон не функционира в режим „SSH криптиране“." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Тези опции са за експертни настройки. Променете само ако сте напълно наясно " +"с техните последици." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Изпълнете 'rsync' посредством '{cmd}':" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" +msgstr "като периодична задача" -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" +msgstr "върху отдалечен хост" + +#: qt/manageprofiles/tab_expert_options.py:86 +#, fuzzy +msgid "when taking a manual backup" +msgstr "когато се изпълнява ръчно моментно копие" + +#: qt/manageprofiles/tab_expert_options.py:110 +#, fuzzy +msgid "Please install 'nocache' to enable this option." +msgstr "Моля инсталирайте 'nocache', за да активирате тази опция" + +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "върху настоящата машина" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Пренасочете стандартният изход към /dev/null при периодичните задачи." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron автоматично ще изпрати имейл с прикачен резултат от cronjobs, ако е " +"инсталиран MTA." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" +"Пренасочете стандартният изход за грешки към /dev/null за периодични задачи." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron автоматично ще изпрати имейл с прикачени грешки на cronjobs, ако е " +"инсталиран MTA." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/сек" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Ограничете използваният трафик от rsync:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "Запазване на ACL (списък за контрол на достъпа)" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Запазване на разширените атрибути (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "Копиране на несигурните връзки (работи само с абсолютни връзки)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Ограничете до една файлова система" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Опциите трябва да бъдат цитирани, напр. {example}." -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Добавете допълнителни опции към rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Префикс за изпълнение преди всяка команда на отдалечената система." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Променливите трябва да бъдат избегнати с \\$FOO. Това не обхваща rsync. Така" +" че, за да добавите префикс за rsync, използвайте \"{example_value}\" с " +"{rsync_options_value}." + +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "по подразбиране" -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" +msgstr "Добавете префикс към SSH командите" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Проверете дали отдалеченият хост е онлайн" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." +msgstr "" +"Внимание: ако е изключено и отдалечената система не е налична, това може да " +"доведе до някои непредвидени грешки." + +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." msgstr "" +"Проверете дали отдалечената система поддържа всички необходими команди." -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Внимание: ако е изключено и отдалечената система не поддържа всички " +"необходими команди, това може да довете до неочаквани грешки." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(по подразбиране: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "деактивирано" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "активирано" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Режим:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Къде да се запазват моментните архиви" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH настройки" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Път:" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "Нов профил" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Парола" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Запазете паролата в ключодържателя" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Кеширайте паролата за периодичните задачи (Възможен проблем със сигурността:" +" потребител root може да достъпва паролата)" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Разширени" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +#, fuzzy +msgid "Full backup path:" +msgstr "Пълен път за моментни архивни копия:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Паролата не съвпада." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Достоверността на системата {host} не може да бъде потвърдена." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} ключовият отпечатък е:" + +#: qt/manageprofiles/tab_general.py:613 +#, fuzzy +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Моля потвърдете този отпечатък! Бихте ли искали да го добавите към " +"'known_hosts' файлът с позволени хостове?" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Създаване на нова криптирана директория?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "" + +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "Частен SSH ключ" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Нов профил" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Преименуване на профил" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Неуспех при създаване на нов SSH ключ в {path}." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Сигурни ли сте, че желаете да изтриете профила \"%s\" ?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Включване на файлове и директории" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, fuzzy, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"\"{path}\" е пряк път. Целта, към която сочи, няма да бъде архивирана докато не добавите и нея.\n" +"Бихте ли искали да добавите крайната цел вместо това?" + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "" + +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Включване на файл" + +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Включване на директории" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Включване на известяването" + +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" +msgstr "Изключване на моментни архиви при работа на батерия" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Енергийното състояние не е налично от системата" + +#: qt/manageprofiles/tab_options.py:52 +#, fuzzy +msgid "Run only one backup at a time" +msgstr "Изпълнявайте по едно моментно копие наведжъж" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:56 +#, fuzzy msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Останалите моментни копия ще бъдат блокирани докато текущото такова не " +"приключи. Това е глобална опция и ще повлияе на всички профили на " +"потребителя. Но трябва да я изберете също и за останалите потребители." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Архивирайте подменените файлове при възстановяване" + +#: qt/manageprofiles/tab_options.py:78 +#, fuzzy +msgid "Continue on errors (keep incomplete backups)" +msgstr "Продължаване при грешки (запазване на непълни моментни архиви)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Използвайте checksum, за да засечете промените" + +#: qt/manageprofiles/tab_options.py:86 +#, fuzzy +msgid "Create a new backup whether there were changes or not." +msgstr "Направете ново моментно копие независимо дали има промени или не." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" msgstr "" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Ниво на записа:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Нищо" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "инструкция за употреба" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, fuzzy, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Следните правила се обработват отгоре надолу. По-късните правила заменят по-" +"ранните и не са ограничени от тях. Вижте {manual} за подробности и примери." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Шаблон за изключване" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Отваряне на инструкцията за употреба я браузъра." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Изключване на файл" +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Запазване на последните архиви." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Изключване на папка" +#: qt/manageprofiles/tab_remove_retention.py:241 +#, fuzzy +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" +"Последният или най-нов архив ще бъде запазен без значение от " +"обстоятелствата." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Включване на файл" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Поведението не може да бъде променено." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Да не се премахват именувани архиви." + +#: qt/manageprofiles/tab_remove_retention.py:258 +#, fuzzy msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Като допълнение на времевият маркер наименувани архиви ще бъдат запазени при" +" всякакви обстоятелства и няма да бъдат изтривани." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Година(и)" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Включване на папка" +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Премахване на моментни архиви по-стари от" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Сигурни ли сте, че жеалете да промените папката за моментни архиви ?" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Пълни дни. Текущият ден се игнорира." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Календарни седмици с понеделник като първи ден. Текущата седмица се " +"игнорира." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12 месечни периоди. Текущият месец се игнорира." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Политика за задържане" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Изпълнява се във фонов режим на отдалечен хост." + +#: qt/manageprofiles/tab_remove_retention.py:313 +#, fuzzy +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"Функцията за умно премахване ще бъде извършена директно от отдалечената " +"машина. Командите \"bash\", \"screen\" и \"flock\" трябва да бъдат " +"инаталирани и достъпни на отдалечената машина." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." msgstr "" +"Ако опцията е избрана, Back In Time първо ще тества отдалечената машина." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Преброяването на дни започва от текущият ден." + +#: qt/manageprofiles/tab_remove_retention.py:322 +#, fuzzy +msgid "Keep all backups for the last" +msgstr "Пазете всички временни архиви за последните" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "ден (дни)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +#, fuzzy +msgid "Keep the last backup for each day for the last" +msgstr "Пази едно моментно копие на ден за последните" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Преброяването на седмиците започва от текущата. Седмицата започва в " +"понеделник." + +#: qt/manageprofiles/tab_remove_retention.py:347 +#, fuzzy +msgid "Keep the last backup for each week for the last" +msgstr "Пазете едно моментно копие на седмица за последните" + +#: qt/manageprofiles/tab_remove_retention.py:352 +#, fuzzy +msgid "week(s)." +msgstr "седмица(и)" + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Преброените месеци са календарни, започвайки с текущия." + +#: qt/manageprofiles/tab_remove_retention.py:360 +#, fuzzy +msgid "Keep the last backup for each month for the last" +msgstr "Пазете едно моментно копие на месец за последните" + +#: qt/manageprofiles/tab_remove_retention.py:365 +#, fuzzy +msgid "month(s)." +msgstr "месец(и)" + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Преброените години са календарни, започвайки с текущата." + +#: qt/manageprofiles/tab_remove_retention.py:372 +#, fuzzy +msgid "Keep the last backup for each year for" +msgstr "Запазете последния моментен архив за всяка година за" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "Всички години." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… свободното пространство е по-малко от" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… свободните inodes са по-малко от" + +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Премахнете най-старите моментни архиви, ако…" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Препратки" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Резевни директории" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Профил: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Профил: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Преглед на последния запис" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Стартиране на {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Изпълняване…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Вмъкване на конфигурация" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Вмъкване" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Изпълняване…" + +#: qt/restoreconfigdialog.py:211 +#, fuzzy, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Изберете директорията за моментни архиви, от която трябва да се черпи " +"конфигурационният файл. Пътят може да изглежда така: {samplePath}" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Ако директорията се намира на външен или отдалечен диск трябва да бъде ръчно" +" монтирана предварително." -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Настройки на сравняването" +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Показване на скритите файлове" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Включване на файлове и директории" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Неуспех при създаването на файл в следната директория:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "" + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Покажи всички записани събития" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "" + +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Изключване на системата след като моментното архивно копие е готово." + +#: qt/shutdowndlg.py:36 +#, fuzzy +msgid "Cancel Shutdown" +msgstr "Изключване" + +#: qt/shutdowndlg.py:37 +#, fuzzy +msgid "Shutdown Now" +msgstr "Изключване" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" + +#: qt/snapshotsdialog.py:61 +#, fuzzy +msgid "Options about comparing backups" +msgstr "Опции за сравняване на моментни архиви" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Команда:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Параметри:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "Използване на %1 и %2 за параметри на пътя" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Изброяване само на различаващите се моментни архиви" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Моля изберете diff команда или изберете Отказ." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Командата {cmd} не е открита на тази система. Моля опитайте друга команда " +"или натиснете Отказ." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Не са въведени параметри за diff команда. Използване на стандартна стойност " +"{params}." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +#, fuzzy +msgid "Backups" +msgstr "&Резервно копие" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Само различаващи се моментни архиви" + +#: qt/snapshotsdialog.py:159 +#, fuzzy +msgid "List only backups that are equal to:" +msgstr "Покажете само съвпадащи моментни архиви до:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Дълбока проверка (по-точна, но бавна)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Изтриване" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Избиране на всички" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Сравняване" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Сравни" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Отиване до" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Не можете да сравнявате моментен архив сам със себе си" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Настройки" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Командата не е открита: %s" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" + +#: qt/snapshotsdialog.py:470 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" +"Наистина ли искате да изтриете {file} от моментния архив {snapshot_id}?" + +#: qt/snapshotsdialog.py:475 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Наистина ли искате да изтриете {file} от {count} моментни архиви?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "Това действие не може да бъде отменено." -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:495 +#, fuzzy, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Изключете {path} от бъдещите моментни архиви?" + +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Файлова система" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Днес" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Вчера" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Тази седмица" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Миналата седмица" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Последна проверка {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#, fuzzy +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Това НЕ е моментно копие, а текущ изглед на локалните Ви файлове" diff --git a/common/po/bs.po b/common/po/bs.po deleted file mode 100644 index 42f716ecd..000000000 --- a/common/po/bs.po +++ /dev/null @@ -1,1647 +0,0 @@ -# Bosnian translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. -# -msgid "" -msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:09+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Bosnian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Profil \"%s\" već postoji !" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Ne možeš izbrisati zadnji profil!" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Isključeno" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Svakih 5 minuta" - -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Svakih 10 minuta" - -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "" - -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "" - -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "" - -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "" - -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "" - -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "" - -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "" - -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Svaki dan" - -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "" - -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Svake sedmice" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Svaki mjesec" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Dan(i)" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Sedmica(e)" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Godina(e)" - -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" - -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" - -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" - -#: ../../common/config.py:129 -msgid "Local" -msgstr "" - -#: ../../common/config.py:130 -msgid "SSH" -msgstr "" - -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "" - -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "" - -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "" - -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" - -#: ../../common/config.py:135 -msgid "Default" -msgstr "" - -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "" - -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "" - -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "" - -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "" - -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "" - -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "" - -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "" - -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "" - -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "" - -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "" - -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "" - -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "" - -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Glavni profil!" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil: \"%s\"" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Folder za slike zaslona nije validan !" - -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Moraš izabrati najmanje jedan folder za rezervu !" - -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Ne možeš dodati folder rezerve !" - -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "" - -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s nije direktorij !" - -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" - -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" - -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" - -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" - -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "" - -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "" - -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" - -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" - -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" - -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" - -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" - -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" - -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" - -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" - -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" - -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "" - -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "" - -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" - -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" - -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" - -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" - -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" - -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" - -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" - -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "" - -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Završeno" - -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" - -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "" -msgstr[1] "" - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "" - -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Nemoguće kreirati direktorij: %s" - -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "" - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Spremi dozvole ..." - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" - -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" - -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Nemoguće izbrisati direktorij: %s" - -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "" - -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" - -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" - -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Pametno brisanje" - -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "" - -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "" - -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" - -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "" - -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Sad" - -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" - -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" - -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" - -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" - -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" - -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" - -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" - -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" - -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" - -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" - -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" - -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" - -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" - -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" - -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" - -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "" - -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "" - -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "" - -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "" - -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "" - -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Postavke" - -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" - -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" - -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "Izlaz" - -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Pomoć" - -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" - -#: ../../qt/app.py:163 -msgid "Website" -msgstr "" - -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" -msgstr "" - -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" - -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "" - -#: ../../qt/app.py:171 -msgid "Report a bug" -msgstr "" - -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "O" - -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Gore" - -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Prikaži skrivene datoteke" - -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Obnovi" - -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" - -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "" - -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" - -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" - -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" - -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" - -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "" - -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" - -#: ../../qt/app.py:271 -msgid "View" -msgstr "" - -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Prečice" - -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" - -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" - -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" - -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" -msgstr "" - -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" -msgstr "" - -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" -msgstr "" - -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "" - -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "" - -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "" - -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" -msgstr "" - -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" -msgstr "" - -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Globalno" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Korijen" - -#: ../../qt/app.py:796 -msgid "Home" -msgstr "" - -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "" - -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" - -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" - -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" - -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" - -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" - -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" - -#: ../../qt/app.py:1083 -#, python-format -msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" -msgstr "" - -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" - -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" - -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" - -#: ../../qt/app.py:1105 -msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" -msgstr "" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Pogledaj trenutni sadržaj diska" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "" - -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" - -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "" - -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" - -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" - -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" - -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "" - -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "" - -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "" - -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "" - -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "" - -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" - -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" - -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" - -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" - -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" - -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" - -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" - -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "" - -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Danas" - -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Jučer" - -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Ove sedmice" - -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Prošle sedmice" - -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" - -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" - -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "" - -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "" - -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" - -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" - -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "" - -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Opšte" - -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "" - -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." -msgstr "" - -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "" - -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" - -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "" - -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "" - -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "" - -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "" - -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "" - -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" - -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "" - -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" - -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" - -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "" - -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" - -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" - -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "" - -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "" - -#: ../../qt/settingsdialog.py:307 -msgid "Schedule" -msgstr "" - -#: ../../qt/settingsdialog.py:317 -msgid "Day:" -msgstr "" - -#: ../../qt/settingsdialog.py:328 -msgid "Weekday:" -msgstr "" - -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "" - -#: ../../qt/settingsdialog.py:350 -msgid "Hours:" -msgstr "" - -#: ../../qt/settingsdialog.py:359 -msgid "" -"Run Back In Time repeatedly. This is useful if the computer is not running " -"regularly." -msgstr "" - -#: ../../qt/settingsdialog.py:364 -msgid "Every:" -msgstr "" - -#: ../../qt/settingsdialog.py:382 -msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" - -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Uključi" - -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" - -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Dodaje datoteku" - -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Dodaj direktorij" - -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Isključi" - -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" - -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" - -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" - -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" - -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" - -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" - -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Automatsko brisanje" - -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Starije od:" - -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Ako je sloboni prostor manji od:" - -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" - -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" - -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" - -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" - -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" - -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "" - -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "" - -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opcije" - -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Uključi obavijesti" - -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" - -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" - -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "" - -#: ../../qt/settingsdialog.py:625 -msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" - -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" - -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" - -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" - -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "" - -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "" - -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "" - -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "" - -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "" - -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" -msgstr "" - -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" -msgstr "" - -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" - -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" - -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" - -#: ../../qt/settingsdialog.py:714 -msgid "on local machine" -msgstr "" - -#: ../../qt/settingsdialog.py:721 -msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "" - -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" - -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "" - -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" - -#: ../../qt/settingsdialog.py:774 -msgid "Preserve ACL" -msgstr "" - -#: ../../qt/settingsdialog.py:787 -msgid "Preserve extended attributes (xattr)" -msgstr "" - -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" - -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" -msgstr "" - -#: ../../qt/settingsdialog.py:839 -msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." -msgstr "" - -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "" - -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" -msgstr "" - -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "" - -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" -msgstr "" - -#: ../../qt/settingsdialog.py:871 -msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." -msgstr "" - -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "" - -#: ../../qt/settingsdialog.py:875 -msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." -msgstr "" - -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "" - -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "" - -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" - -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "" - -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "" - -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "" - -#: ../../qt/settingsdialog.py:1201 -msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" -msgstr "" - -#: ../../qt/settingsdialog.py:1237 -msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" -msgstr "" - -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" - -#: ../../qt/settingsdialog.py:1360 -msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" -msgstr "" - -#: ../../qt/settingsdialog.py:1376 -#, python-format -msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" -msgstr "" - -#: ../../qt/settingsdialog.py:1384 -msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" -msgstr "" - -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "" - -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "" - -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "" - -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "" - -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format -msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" -msgstr "" - -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Uključi direktorij" - -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "" - -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" - -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "" - -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" - -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "" - -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr "" - -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" - -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "" - -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "" - -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "" - -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "" - -#: ../../qt/snapshotsdialog.py:60 -msgid "Command:" -msgstr "Komanda:" - -#: ../../qt/snapshotsdialog.py:64 -msgid "Parameters:" -msgstr "Parametri:" - -#: ../../qt/snapshotsdialog.py:68 -msgid "Use %1 and %2 for path parameters" -msgstr "Koristite %1 i %2 za parametre putanje" - -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "" - -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "" - -#: ../../qt/snapshotsdialog.py:128 -msgid "Deep check (more accurate, but slow)" -msgstr "" - -#: ../../qt/snapshotsdialog.py:149 -msgid "Delete" -msgstr "" - -#: ../../qt/snapshotsdialog.py:153 -msgid "Select All" -msgstr "" - -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "" - -#: ../../qt/snapshotsdialog.py:177 -msgid "Go To" -msgstr "Idi na" - -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "" - -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Komanda nije pronađena: %s" - -#: ../../qt/snapshotsdialog.py:361 -#, python-format -msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" -msgstr "" - -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" -msgstr "" - -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "" - -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "" diff --git a/common/po/ca.po b/common/po/ca.po index 172f9417a..315ba91a1 100644 --- a/common/po/ca.po +++ b/common/po/ca.po @@ -1,1668 +1,2696 @@ -# Catalan translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Josep Sanchez +# SPDX-FileCopyrightText: © Adolfo Jayme Barrientos , 2025 +# SPDX-FileCopyrightText: © Pablo Huguet # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-10-30 17:57+0000\n" -"Last-Translator: Mario Rodrigo \n" -"Language-Team: Catalan \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-02 11:33+0000\n" +"Last-Translator: Voodoo_Pablo \n" +"Language-Team: Catalan \n" +"Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"Totes les llicències utilitzades en aquest projecte es troben al directori " +"{dir_link}. Per extreure informació de llicència i drets d'autor per fitxer " +"mitjançant metadades SPDX, consulteu {readme_link}." -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "El perfil \"%s\" ja existeix !" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "No es pot esborrar el darrer perfil !" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Avís" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Deshabilitat" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Perfil principal" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "En cada inici/reinici" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Local (xifrat amb EncFS)" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Cada 5 minuts" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (xifrat amb EncFS)" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Cada 10 minuts" +#: common/config.py:237 +msgid "Local" +msgstr "Local" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Cada 30 minuts" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Xifrat local" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Cada hora" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Xifratge" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Cada 2 hores" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Cada 4 hores" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Clau privada SSH" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Cada 6 hores" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Perfil: «{name}»" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Cada 12 hores" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "El directori de còpies no és vàlid." -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Hores personalitzades" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Almenys cal seleccionar un directori per la copia de seguretat." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Cada dia" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Directori: {path}" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Aquest directori no es pot incloure en la còpia de seguretat perquè forma " +"part de la destinació mateixa de la còpia." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"El valor de \"Elimina la còpia de seguretat més antiga si l'espai lliure és " +"inferior a\" ({val_one}) ha de ser inferior o igual al llindar de \"Avisa si" +" l'espai lliure en disc cau per sota de\" ({val_two})." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Cada setmana" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Cada mes" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Dia(es)" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Setmana(es)" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Any(s)" - -#: ../../common/config.py:102 -msgid "Hour(s)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" +"Ajusteu la configuració de manera que el límit d'eliminació de còpies de " +"seguretat no sigui superior al límit d'advertència." -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "El planificador udev no funciona amb el mode {mode}" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " EXPERIMENTAL!" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "No s'ha pogut escriure el nou crontab ." -#: ../../common/config.py:129 -msgid "Local" -msgstr "Local" +#: common/config.py:1577 +#, fuzzy +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"El cron no s'executa tot i que l'ordre crontab està disponible. Les tasques " +"de còpia de seguretat programades no s'executaran. El cron pot estar " +"instal·lat però no activat. Proveu l'ordre «systemctl enable cron» o " +"consulteu els canals d'assistència de la vostra distribució GNU/Linux." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "No s'ha pogut desar la configuració" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "No s'ha pogut carregar la configuració" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "El perfil «{name}» ja existeix." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "No es pot eliminar l'últim perfil." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "No s'ha pogut muntar «{command}»" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "No s'ha trobat la configuració de la carpeta xifrada." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Voleu crear una nova carpeta xifrada?" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Clau privada SSH" +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Canceŀla" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Xifrat local" +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "Introduïu una contrasenya per a «{user}»." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Xifrat" +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "La contrasenya no coincideix." -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "Xifrat amb SSH" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Fes una instantània" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Predeterminat" +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "No s'ha pogut muntar «{command}»" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "No es pot desmuntar {mountprocess} des de {mountpoint}." -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"No s'ha trobat {command}. Instal·leu-lo (p. ex. amb «{installcommand}»)" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "El punt de muntatge {mntpoint} no és buit." -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Introduïu la contrasenya del perfil {mode} «{profile}»:" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/schedule.py:238 +#, fuzzy, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"No s'ha pogut instal·lar la regla d'Udev per al perfil {profile_id}. El " +"servei de DBus Service {dbus_interface} no estava disponible" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "No s'ha pogut trobar l'UUID per a {path}" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "S'HA PRODUÏT UN ERROR" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Restaura els permisos" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Fet" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Les entrades següents de la llista d'inclusions no tenen cap fitxer o " +"directori corresponent a l'origen de la còpia de seguretat:" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Es posposa la còpia de seguretat mentre s'utilitza la bateria" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "No es pot trobar el directori de còpies." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Perfil principal" +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "Si es troba en una unitat extraïble, connecteu-la." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Perfil: \"%s\"" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Esperant {n} segon." +msgstr[1] "Esperant {n} segons." -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "La carpeta d'instantànies no es vàlida!" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "No s'ha pogut fer la còpia {snapshot_id}." -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Heu de seleccionar al menys una carpeta per salvaguardar!" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Si us plau, tingueu paciència. S'està finalitzant…" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "No podeu incloure la carpeta de còpies de seguretat!" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "No es pot crear la carpeta." -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "No podeu incloure una subcarpeta de còpies de seguretat!" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "S'està desant el fitxer de configuració …" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s no és una carpeta!" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "S'estan desant els permisos …" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "S’ha trobat la còpia incompleta «{snapshot_id}» que es pot continuar." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +#: common/snapshots.py:1401 +#, fuzzy, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" msgstr "" -"No es pot escriure a: %s\n" -"Esteu segurs de que teniu permisos d'escriptura ?" +"S'està suprimint la carpeta sobrant de l'última execució: {snapshot_id}" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "No es pot eliminar el directori" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "S'està creant la còpia" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Copia enllaços (desarbitra els enllaços simbòlics)" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Èxit" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Opcions avançades" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Transferència parcial deguda a un error" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" +"Transferència parcial a causa de la desaparició dels fitxers d'origen (mireu" +" «man rsync»)" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"No es pot trobar el 'crontab'.\n" -"Esteu segur que el 'cron' està instal·lat ?\n" -"Si no és així, deveu desactivar totes les còpies automàtiques." +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "«rsync» ha finalitzat amb el codi de sortida {exit_code}" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Consulteu «man rsync» per a més detalls" -#: ../../common/config.py:1575 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" +"Els codis de sortida negatius de l'rsync són números de senyal; vegeu «kill " +"-l» i «man kill»" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "No hi ha canvis; no cal una còpia nova" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" -"No s'ha pogut muntar '%(command)s':\n" -"\n" -"%(error)s" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "No es pot canviar el nom de {new_path} a {path}." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "No s'ha trobat la configuració de la carpeta xifrada." +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "Aplica regles per a eliminar còpies antigues" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Voleu crear una nova carpeta xifrada?" +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "Aplicar la política de retenció" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Canceŀla" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Proveu de mantenir un minim d'espai lliure" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Confirmeu la contrasenya" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Proveu de mantenir mínim un {perc} d'inodes lliures" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "La contrasenya no concorda" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Ara" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "No s'ha pogut muntar {sshfs}" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Fes una instantània" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "No s'ha trobat l'ssh-agent. Assegureu-vos que està instal·lat." -#: ../../common/mount.py:415 -#, python-format +#: common/sshtools.py:489 msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"No s'ha pogut desbloquejar la clau privada ssh. O la contrasenya és " +"incorrecta o no és disponible per al cron." -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "El camí remot existeix però no és un directori." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" -"%(user)s no és membre del grup 'fuse'.\n" -" Executeu 'sudo adduser %(user)s fuse'. Per aplicar els canvis cal que " -"sortiu i torneu a entrar a la sessió.\n" -"Mireu a 'man backintime' per a més instruccions." +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "No es pot escriure al camí remot." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "el punt de muntatge %s no està buit." +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "El camí remot no és executable." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "No s'ha pogut crear el camí remot." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "L'amfitrió remot {host} no admet {command}" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" +"Comproveu les ordres a l'amfitrió {host} ha retornat un error desconegut" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "HA FALLAT" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Restaura els permisos:" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "L'amfitrió remot {host} no admet hardlinks" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Fet" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Copieu la clau SSH pública «{pubkey}» a l'amfitrió remot «{host}»." -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "Postposant la còpia de seguretat mentre s'utilitza la bateria" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Introduïu una contrasenya per a «{user}»." -#: ../../common/snapshots.py:669 +#: common/tools.py:382 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"No es pot trobar la carpeta d'instantànies.\n" -"Si està en una unitat extraïble, connecteu-la." - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Esperant %s segon." -msgstr[1] "Esperant %s segons." +"El sistema de fitxers de destinació per a {path} està formatat amb NTFS, que" +" té incompatibilitats conegudes amb sistemes de fitxers d'estil Unix." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "S'ha produït un error fent la instantània %s!!!" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} no és un directori vàlid." -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Finalitzant" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "La creació del directori següent ha fallat:" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "No es pot crear la carpeta: %s" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "L'accés d'escriptura pot estar restringit." -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Guarda el fixter de configuració ..." - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Guarda els permisos ..." - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"El sistema de fitxers de destinació per a {path} està formatat amb FAT que " +"no admet enllaços durs. Si us plau, utilitzeu un sistema de fitxers " +"GNU/Linux natiu." -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"El sistema de fitxers de destinació per a {path} és una compartició muntada " +"amb SMB. Assegureu-vos que el servidor remot SMB admet enllaços simbòlics o " +"activeu «{copyLinks}» a «{expertOptions}»." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "No es pot eliminar la carpeta: %s" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Copia enllaços (dereferencia els enllaços simbòlics)" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Fes una instantània" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Opcions avançades" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"El sistema de fitxers de destí per a {path} és una compartició muntada amb " +"sshfs. Sshfs no admet hard-links. Utilitzeu el mode 'SSH' en el seu lloc." -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "La creació del fitxer ha fallat en aquest directori:" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Eliminació intel·ligent" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Quant al Back in Time" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Elimina les instantànies antigues" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Drets de reproducció:" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Proveu de mantenir un minim d'espai lliure" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Autoria:" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Traducció:" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "AMB ERRORS !" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "" +"Josep Sanchez \n" +"Adolfo Jayme Barrientos , 2025\n" +"Pablo Huguet " -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Ara" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Crèdits de traductor no disponibles per a l'idioma actual." -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "No s'ha pogut muntar %s" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "aquest enllaç" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" +"Segueix {thislink} per obtenir crèdits de traductor per a tots els idiomes." -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Lloc web del projecte" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Manual d'ús" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." +#: qt/aboutdlg.py:247 qt/app.py:546 +#, fuzzy +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Obre el manual d'usuari al navegador (local si està disponible en línia)" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Versió{BOLDEND}: {version}" -#: ../../common/sshtools.py:470 -#, python-format +#: qt/app.py:332 +#, fuzzy, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"{app_name} sembla que sigui el primer cop que s'executa perquè no s'ha " +"trobat cap configuració." -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:337 +#, fuzzy msgid "" -"Remote path is not executable:\n" -" %s" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Importeu una configuració existent (des d'una carpeta d'una còpia de " +"seguretat o d'un altre ordinador)?" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "A continuació, premeu D'acord." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Crea una còpia" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" +"Utilitza el temps de modificació i mida per a la detecció de canvis de " +"fitxers." -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Crea una còpia (mode suma de verificació)" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Utilitza sumes de verificació per a la detecció de canvis de fitxers." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Pausa el procés de còpia" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Reprèn el procés de còpia" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Utilitza el checksum per detectar canvis" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Atura el procés de còpia" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Actualitza la llista de còpies" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Còpia de seguretat del nom" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Elimina la còpia" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Refresca la llista d'instantànies" +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "Visualitza l'últim registre" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Nom de la instantània" +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "Almenys cal seleccionar un directori per la copia de seguretat." -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Elimina la instantània" +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" +msgstr "Visualitza l'últim registre" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Visualitza el log d'instantànies" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Veure el registre de la darrera còpia de seguretat." -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Visualitza l'últim log" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Gestiona els perfils…" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Configuració" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Edita user-callback" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" -msgstr "" +msgstr "Aturada" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Apaga el sistema després d'acabar la còpia." + +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Configura la llengua…" -#: ../../qt/app.py:149 +#: qt/app.py:540 msgid "Exit" -msgstr "Sortir" +msgstr "Surt" + +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "pàgina de manual: Back In Time" + +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Mostra la pàgina de manual sobre Back In Time (backintime)" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Ajuda" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "Página Manual: Perfils de config file" -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" +"Mostra la pàgina de manual sobre el fitxer de configuració de perfils " +"(backintime-config)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Lloc web" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Obre el lloc web del Back In Time al navegador" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" +msgstr "Registre de canvis" + +#: qt/app.py:570 +#, fuzzy +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" +"Obre el manual d'usuari al navegador (local si està disponible en línia)" -#: ../../qt/app.py:167 +#: qt/app.py:573 msgid "FAQ" -msgstr "PMF" +msgstr "Preguntes freqüents" + +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Obriu Preguntes freqüents (FAQ) al navegador" -#: ../../qt/app.py:169 +#: qt/app.py:577 msgid "Ask a question" -msgstr "Fer una pregunta" +msgstr "Feu una pregunta" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Informar d'un error" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "Quant a" +#: qt/app.py:584 +msgid "Translation" +msgstr "Traducció" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Amunt" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Mostra de nou el missatge sobre la participació en la traducció." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Mostra els fitxers ocults" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Transició del xifratge (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Mostra de nou el missatge sobre l'eliminació d'EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "Quant a" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Restaura" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." msgstr "" +"Restaura els fitxers o carpetes seleccionades a la destinació original." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Restaura a..." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Restaura a …" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "Restaura els fitxers o carpetes seleccionades a una destinació nova." -#: ../../qt/app.py:234 +#: qt/app.py:615 +#, fuzzy msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Restaura la carpeta mostrada actualment i tot el seu contingut a la " +"destinació original." -#: ../../qt/app.py:239 +#: qt/app.py:621 +#, fuzzy msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" +"Restaura la carpeta mostrada actualment i tot el seu contingut a una nova " +"destinació." -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "Amunt" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Instantànies" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Mostra els fitxers ocults" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Compara les còpies…" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Candidat d'alliberament" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Dreceres" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Torna a mostrar el missatge sobre aquest candidat a la versió." -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Còpia de seguretat" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Restaura" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Ajuda" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Icona Systray" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automàtic" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Icona Light" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Icona Dark" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Només icones" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Només text" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Text a sota de les icones" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Text al costat de la icona" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Aquesta carpeta no existeix\n" +"a la snapshot seleccionada." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"No es pot trobar la carpeta d'instantànies.\n" -"Si es troba en una unitat extraïble, connecteu-la i premeu OK" +"Aquesta carpeta no existeix\n" +"a la snapshot seleccionada." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Si tanqueu aquesta finestra, el Back In Time no podrà apagar el sistema quan" +" s'hagi finalitzat la còpia." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "S'està treballant:" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Voleu tancar la finestra de totes maneres?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" msgstr "Fet, no ha estat necessari crear una còpia de seguretat" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "S'ha produït un error:" +#: qt/app.py:1260 +msgid "Working:" +msgstr "S'està treballant:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Treballant" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Error" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "Enviat:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "" +msgstr "Velocitat:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" +msgstr "ETA:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Global" +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "Còpies:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Arrel" - -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Inici" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Restaura {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Carpetes de còpia de seguretat" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Restaura {path} a …" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Esteu segur que voleu eliminar la instantània:\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Hola\n" +"Heu utilitzat el Back In Time en l'idioma {language} unes quantes vegades.\n" +"La traducció de la vostra versió instal·lada de Back In Time a {language} està un {perc} completada. Independentment del seu nivell d'experiència tècnica, pot contribuir a la traducció i, per tant, en Back In Time mateix.\n" +"Visiteu {translation_platform_url} si voleu col·laborar. Per obtenir més assistència i preguntes, visiteu {back_in_time_project_website}.\n" +"Demanem disculpes per la interrupció i aquest missatge no es tornarà a mostrar. Aquest diàleg està disponible en qualsevol moment a través del menú d'ajuda.\n" +"L'equip de Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "plataforma de traducció" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Lloc web" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "la teva traducció" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Al fedivers, a Mastodon: {link_and_label}." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Llista de correu {link_and_label}" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/app.py:1718 +#, fuzzy, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Llista de correu {link_and_label}" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} al lloc web del projecte." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Obre un problema" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Alternativament, podeu utilitzar un altre canal que trieu." -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Aquesta versió de Back In Time és una versió candidata i està destinada principalment a proves d'estabilitat en preparació per al proper llançament oficial.\n" +"No es recullen dades d'usuari ni telemetria. Tanmateix, l'equip de Back In Time està molt interessat a saber si s'està utilitzant el Release Candidate i si val la pena continuar proporcionant aquestes versions prèvies.\n" +"Per tant, l'equip demana amablement un breu comentari sobre si heu provat aquesta versió, fins i tot si no heu trobat cap problema. Fins i tot una prova ràpida d'uns minuts ens ajudaria molt.\n" +"Les opcions de contacte següents estan disponibles:\n" +"{contact_list}\n" +"En aquesta versió, aquest missatge no es tornarà a mostrar, però es pot accedir en qualsevol moment mitjançant el menú d'ajuda.\n" +"Gràcies pel vostre suport i per ajudar-nos a millorar Back In Time!\n" +"El teu equip de Back In Time" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Només hi ha {free} espai lliure disponible a la destinació, que està per " +"sota del llindar configurat de {threshold}." -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/app.py:1841 +#, fuzzy +msgid "Proceed with the backup?" +msgstr "Voleu eliminar aquesta còpia?" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Tots els fitxers més nous de {path} s'eliminaran. Voleu continuar?" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Tots els fitxers més nous del directori original s'eliminaran. Voleu " +"continuar?" -#: ../../qt/app.py:1105 +#: qt/app.py:1875 +#, fuzzy, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}AVÍS:{BOLDEND} Esborrar fitxers a l'arrel del sistema de fitxers " +"podria trencar tot el sistema." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Visualitza el contingut actual del disc" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Nom de la còpia" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Instantània: %s" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Voleu eliminar aquesta còpia?" +msgstr[1] "Voleu eliminar aquestes còpies?" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Visualitza la instantània creada a %s" - -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Restaura '%s'" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "" +"La configuració de la llengua només tindrà efecte després de reiniciar Back " +"In Time." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Restaura '%s' a..." +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Còpies de seguretat versionades (root)" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"GUI fàcil d'utilitzar per a còpies de seguretat versionades que redueix l'ús" +" del disc (mode root)" + +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "No eliminis les snapshots amb nom." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"GUI fàcil d'utilitzar per a còpies de seguretat versionades que redueix l'ús" +" del disc" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Pregunta" + +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Crea còpies de seguretat amb {suffix} al final abans de\n" +"sobreescriure o esborrar fitxers locals." -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, fuzzy, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Les versions més noves dels fitxers es canviaran de nom amb el final " +"{suffix} abans de restaurar. Si ja no els necessiteu, podeu eliminar-los amb" +" la següent comanda:" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Restaura només els fitxers que no existeixen o\n" +"són més nous que els de destí.\n" +"S'utilitza l'opció «rsync --update»." -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "Perfil:" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Elimina els elements més nous del directori original." -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "Filtra:" +#: qt/confirmrestoredialog.py:135 +#, fuzzy +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Restaura els fitxers o carpetes seleccionats al destí original i suprimeix " +"els fitxers o directoris que no són a la instantània. Aneu amb compte perquè" +" això suprimirà els fitxers i carpetes exclosos de la instantània." + +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Segur que voleu restaurar aquest element al directori nou?" +msgstr[1] "Segur que voleu restaurar aquests elements al directori nou?" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Segur que voleu restaurar aquest element?" +msgstr[1] "Segur que voleu restaurar aquests elements?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" +"L'script de crida de retorn de l'usuari ha d'incloure un shebang a la " +"primera línia (per exemple, {example})." -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Inclou fitxers i carpetes" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Afegeix a la inclusió" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Afegeix a l'exclusió" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Seleccioneu entre els elements d'ús comú per afegir-los a la llista " +"d'exclusions." +msgstr[1] "" +"Seleccioneu entre els elements d'ús comú per afegir-los a la llista " +"d'exclusions." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Seleccioneu entre els elements d'ús comú per afegir-los a la llista " +"d'exclusions." +msgstr[1] "" +"Seleccioneu entre els elements d'ús comú per afegir-los a la llista " +"d'exclusions." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Llenguatge de configuració" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Traduït: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "sistema per defecte" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Utilitza la llengua del sistema operatiu." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "Última visualització dels registres" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Última visualització dels registres" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "Perfil:" + +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Còpies:" + +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "Filtre:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E]Error, [I]Informació, [C]Canvia" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "descodifica rutes" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Tots" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Canvis" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Errors" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Canvis" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informació" +msgstr[1] "Informacions" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informacions" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "Errors de transferència de rsync (experimental)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E]Errors, [I]Informació, [C]Canvia" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Gestiona els perfils" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Edita" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Afegeix" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Elimina" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&General" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Inclou" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Exclou" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Eliminar & &Retenir" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "S'està treballant..." +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Opcions" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Avui" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "Opcions d'e&xperts" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Ahir" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Restaura la configuració" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Aquesta setmana" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Perfil nou" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "La setmana passada" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Reanomena el perfil" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Voleu suprimir el perfil «{name}»?" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Copiar enllaços simbòlics com a fitxers" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Copia els enllaços simbòlics com a fitxers o directoris reals a la còpia de " +"seguretat. Selecciona si es copien tots els enllaços o només els que apunten" +" fora de l'origen." + +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Aquesta opció pot augmentar la mida de la còpia de seguretat." -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Desactivat per defecte." + +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Tots els enllaços simbòlics es substitueixen per fitxers o directoris reals " +"als quals apunten. Això augmenta la mida de la còpia de seguretat i pot " +"emmagatzemar els mateixos fitxers diverses vegades." -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Edita" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Només extern" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"Només es copien com a fitxers els enllaços que apunten fora de l'origen de " +"la còpia de seguretat. Això augmenta la mida de la còpia de seguretat i pot " +"emmagatzemar els mateixos fitxers diverses vegades." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Editor i fitxers temporals d'Office" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "General" +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "Pausa el procés de còpia" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Mode:" +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "Exclou el fitxer" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Fitxers d'intercanvi de Vim" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "A on guardar Instanànies" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Fitxers temporals de Microsoft Office" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "Fitxers de bloqueig de LibreOffice i altres editors d'OpenDocument" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniatures i imatges temporals" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" +"Memòria cau de miniatures a GNU/Linux i altres sistemes operatius Unixoid" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Ordinador amfitrió:" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Base de dades de miniatures a Windows" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Port:" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Directori de metadades a MacOS" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Usuari:" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Application-specific bloquejats" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Camí:" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Fitxer de bloqueig de l'aplicació Discord" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "Xifrat:" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Fitxer de bloqueig de sessió de Discord" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Clau privada:" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Fitxer de bloqueig de Mozilla Firefox i Thunderbird" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Directoris de còpia de seguretat" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Cache de l'aplicació de l'Usuari" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Contrasenya" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "No es pot eliminar el directori" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Cache dels paquets per a distribucions GNU/Linux basades en Debian" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Repositori d'aplicacions i temps d'execució de Flatpak" + +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Mostra els fitxers ocults" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Informació del kernel i del procés" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" +"Informació del dispositiu i altra informació del maquinari (interfície " +"sysfs)" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Avançat" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Nodes de dispositiu" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Fitxers del sistema en temps d'execució" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Altres no persistents" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Llista de sistemes de fitxers muntats actualment" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Fitxer d'intercanvi del sistema (memòria virtual)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "Punt de muntatge del sistema de fitxers virtual del GNOME" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Objectes del sistema de fitxers recuperats" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Miscelanea" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Directori de metadades a Microsoft Windows" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Paperera de reciclatge de l'usuari" + +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Atura el procés de còpia" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Exclou el directori" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" +"Seleccioneu els elements que s'utilitzen habitualment per afegir-los a les " +"exclusions de còpia de seguretat." + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "per defecte" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Restableix a la selecció predefinida" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Planificació" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "Dia:" +msgstr "Día:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "Dia de la setmana:" +msgstr "Entre setmana:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Hora:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Temps:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "Hores:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "després de l'hora" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minuts:" + +#: qt/manageprofiles/schedulewidget.py:93 +#, fuzzy +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Executa Back in Time bon punt es connecti la unitat (només una vegada cada X" +" dies). Se us demanarà la contrasenya del sudo." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Executa Back In Time repetidament. Això és útil si l'ordinador no s'està " +"executant regularment." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" +msgstr "Cada:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Activa el registre dels missatges de depuració" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Escriu missatges de nivell de depuració al registre del sistema mitjançant " +"«--debug»." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Precaució: utilitzeu-lo només temporalment per a diagnòstics, ja que genera " +"una gran quantitat de sortida." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Inclou" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Deshabilitat" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Inclou fitxers i carpetes" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "En cada inici/reinici" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Afegeix un fitxer" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Cada minut" +msgstr[1] "Cada {n} minuts" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Cada hora" +msgstr[1] "Cada {n} hores" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Afegeix un directori" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Hores personalitzades" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Exclou" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Cada dia" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Repetidament (anacron)" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Exclou fitxers i carpetes" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Quan la unitat es connecti (udev)" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Molt recomanat" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Cada setmana" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Cada mes" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Cada any" -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Hora(es)" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Eliminació automàtica" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Dia(es)" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Més antics que:" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Setmana(es)" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Si l'espai lliure és inferior a:" +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Mes(os)" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Les hores personalitzades només poden ser una llista d'hores separades per " +"comes (p.e. 8,12,18,23) o */3 per fer còpies periòdiques cada 3 hores." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "Ponechat všechny zachycené stavy za minulých" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Zvolte existující soubor se soukromým klíčem odjinud." -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "dnů" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "Ponechat jeden zachycený stav co den za minulých" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Vytvořit nový SSH klíč (bez heslové fráze)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "Ponechat jeden zachycený stav co týden za minulých" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Úplný popis umístění: {path}" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "týdnů" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Soukromý klíč:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "Ponechat jeden zachycený stav co měsíc za minulých" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Použít nastavení SSH ze systému" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "měsíců" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" +"Ponechá soubor s klíčem nevybrán. Připojení k SSH budou spoléhat na " +"existující nastavení klienta v systému (např. ~/.ssh/config)." -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "Ponechat jeden zachycený stav co rok za všechna leta" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH proxy" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Neodstraňovat ty zachycené stavy, které uživatel nějak nazval" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Stroj:" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Předvolby" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Uživatel:" + +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" -"Zobrazovat oznámení (prostřednictvím centra oznámení desktopového prostředí)" +"Připojit k cíli prostřednictvím této proxy (známé také jako tzv. „jump " +"host“). Podrobnosti viz „-J“ v dokumentaci příkazu „ssh“ nebo „ProxyJump“ v " +"manuálové stránce „ssh_config“." -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Vypnout pořizování zachycených stavů při provozu na akumulátor" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Pro informaci{ENDBOLD}: V režimu „Přes SSH (v šifrované podobě)“ " +"funguje pouze jedna nebo dvě hvězdičky (např. {example2}). Ostatní typy " +"zástupných vyjádření a vzorů budou ignorovány (např. {example1}) Názvy " +"souborů se totiž v tomto režimu kvůli EncFS šifrování nedají předvídat." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Vynechat zástupně vyjádřené, soubory a složky" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Údaje o stavu napájení nejsou dispozici" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Přidat vzor" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "Pořizovat pouze jeden zachycený stav naráz" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Přidat soubory" -#: ../../qt/settingsdialog.py:625 -msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Přidat složky" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Doporučení" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -"Pořizování ostatních zachycených stavů bude blokováno do dokončení toho " -"stávajícího.\n" -"Toto je celková předvolba. Takže se týká všech profilů tohoto uživatele.\n" -"Ale zapíná se pro každého uživatele zvlášť." +"Vyberte z běžně používaných položek pro přidání do seznamu vynechávaných." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "Zazálohovat soubory, které by byly při procesu obnovování nahrazeny" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Vynechat soubory větší než:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Vynechat soubory větší než hodnota v {size_unit}." -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" -"Pokračovat i v případě výskytu chyb (zachovat i neúplné zachycené stavy)" +"V případě, že není používán „Úplný rsync režim“, bude se toto týkat pouze " +"nově vytvořených souborů, protože pro nástroj rsync je toto předvolba " +"přenosu, nikoli vynechání. Takže, velké soubory, které byly zazálohovány už " +"dříve, v zálohách zůstanou, i když od té doby byly změněny." -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "Pořídit zachycený stav i v případě, že nedošlo k žádným změnám." +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Zástupné vyjádření vynechaného" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Podrobnost zaznamenávání událostí (log):" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Zadejte vzor vynechávaného:" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Nic" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Ohledně nápovědy nahlédněte do manuálové stránky rsync, sekce {link}." + +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Otevřít manuálovou stránku rsync" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Změny a chyby" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Vynechat soubory" + +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Vynechat složky" + +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" +"Vypnuto protože tento vzor není funkční v režimu „Přes SSH (v šifrované " +"podobě)“." -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Tyto předvolby měňte pouze v případě, že opravdu víte co děláte!" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"Tyto předvolby jsou pro pokročilá nastavení. Měňte je pouze pokud jste si " +"plně vědomi jejich důsledků." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "Spustit s nižší prioritou (nice):" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Spouštět nástroj rsync s parametrem „{cmd}“:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" msgstr "když jako naplánovaná úloha (cron)" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "při zálohování na jiný stroj" +msgstr "při zálohování na vzdálený stroj" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "Spustit s nižší prioritou na vst./výst. (ionice):" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "při ručně spuštěné záloze" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "při ručně spuštěném pořízení zachyceného stavu" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Pokud chcete zapnout tuto možnost, nainstalujte nástroj „nocache“." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "Spustit nástroj rsync s parametrem nocache:" - -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" msgstr "při ukládání záloh přímo na zálohovaném počítači" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" "U naplánovaných úloh (cron) zahazovat výstup (stdout) (přesměrováním do " "/dev/null)." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" +"Pokud je nainstalován (a nastaven) MTA, cron automaticky pošle e-mail s " +"přiloženým výstupem z naplánovaných úloh." + +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" -"U naplánovaných úloh (cron) zahazovat chybový výstup (stderr) (přesměrováním " -"do /dev/null)." +"U naplánovaných úloh (cron) zahazovat chybový výstup (stderr) (přesměrováním" +" do /dev/null)." + +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" +"Pokud je nainstalován (a nastaven) MTA, cron automaticky pošle e-mail s " +"přiloženými chybami naplánovaných úloh." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "Omezit využití přenosové kapacity, zabrané nástrojem rsync, na: " +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/s" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr " KB/s" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Omezit využití přenosové kapacity, zabrané nástrojem rsync, na:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "Zálohovat včetně seznamů řízení přístupu (ACL)" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Zálohovat včetně rozšířených atributů (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" -"V případě odkazů na soubory/složky, nacházející se mimo zálohovanou složku, " -"zazálohovat přímo je a nikoli jen odkaz (funguje pouze s absolutními odkazy)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Nepřekračovat (přípojnými body) do dalších souborových systémů" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Je třeba, aby předvolby byly obklopeny uvozovkami, např. {example}." -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "Dodatečné parametry, se kterými spouštět nástroj rsync:" +msgstr "Vložte dodatečné parametry pro rsync" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Předpona kterou spouštět před každým příkazem na vzdáleném stroji." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -"Je třeba, aby předvolby byly obklopeny uvozovkami, např. --exclude-" -"from=\"/umisteni/vynechany_soubor\"." +"Proměnné je třeba zbavit jejich významu pomocí zpětného lomítka (příklad: " +"\\$NECO). Toto se nedotýká nástroje rsync. Předponu pro rsync tedy přidáte " +"pomocí „{example_value}“ s {rsync_options_value}." -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "Před příkazy přes SSH přidávat:" - -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" -msgstr "" -"Čím uvést každý z příkazů, spouštěným na stroji na protějšku.\n" -"Proměnné je třeba zbavit jejich významu pomocí zpětného lomítka (příklad: \\" -"$NECO).\n" -"Toto se nedotýká nástroje rsync. Pro ten použijte předvolbu " -"„%(cbRsyncOptions)s“ s\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" - -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 +#: qt/manageprofiles/tab_expert_options.py:293 msgid "default" msgstr "výchozí" -#: ../../qt/settingsdialog.py:870 +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Před příkazy přes SSH přidávat" + +#: qt/manageprofiles/tab_expert_options.py:308 msgid "Check if remote host is online" -msgstr "Zkontrolujte zda je stroj na protistraně dostupný na síti" +msgstr "Zkontrolujte zda je vzdálený stroj dostupný na síti" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" -"Varování: pokud je vypnuté a stroj na protějšku\n" -"není dostupný, může toto vést k některým\n" -"podivným chybám." +"Varování: pokud vypnuto a vzdálený stroj není dostupný, může toto vést k " +"některým podivným chybám." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "Ověřte zda stroj na protistraně podporuje všechny nezbytné příkazy" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Ověřit zda vzdálený stroj podporuje všechny nezbytné příkazy." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" -"Varování: pokud je vypnuto a stroj na protějšku\n" -"nepodporuje veškeré nezbytné příkazy,\n" -"může toto vést k některým podivným chybám." +"Varování: pokud vypnuto a vzdálený stroj nepodporuje veškeré nezbytné " +"příkazy, může toto vést k některým podivným chybám." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "Obnovit nastavení" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(výchozí: {})" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "Upravit uživatelsky definované akce" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "vypnuto" -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" -"Zachycený stav, pořízený pomocí funkce kompletní záloha systému, je s " -"jistotou možné obnovit pouze na stejnou jednotku datového úložiště, ze které " -"byl pořízena a to ještě za předpokladu, že rozvržení oddílů na ní bude " -"stejné, jaké je v zachyceném stavu. Pokus o obnovení na jednotku lišící se " -"kapacitou a/nebo rozvržením oddílů může vyústit v rozbitý a tím nepoužitelný " -"systém.\n" -"\n" -"Úprava profilu pro účely kompletní zálohy systému může vést ke změně " -"některých nastavení, které jste v něm mohli udělat. Přesto pokračovat?" - -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Nový profil" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "zapnuto" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Přejmenovat profil" +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Režim:" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Opravdu chcete smazat profil „%s“?" +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Kam ukládat zálohy" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Nastavení pro SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Umístění:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Soubor s klíčem:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Heslo pro" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Uložit heslo do klíčenky desktopového prostředí" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" +"Uložit heslo pro potřeby plánovače Cron (slabina v zabezpečení: správce " +"systému si heslo může přečíst)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Pokročilé" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Úplný popis umístění zálohy:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" +"Plánování je vypnuté protože nebyla nalezena instalace plánovače cron. Pokud" +" chcete zapnout plánování záloh, nainstalujte ho." + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Je třeba, aby popis umístění cíle zálohy nebyl prázdný." + +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Šifrovací heslo je třeba vyplnit." + +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -"Vlastní doby mohou být jen čárkou oddělovaný seznam hodin (například " -"8,12,18,23) nebo (např.) */3 pro pravidelné zálohy každé 3 hodiny" +"Došlo k chybě při pokusu o přihlášení se ke vzdálenému stroji. Byla vrácena " +"následující chybová zpráva:" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -"Nezvolili jste soukromou část klíče pro SSH.\n" -"Přejete si vytvořit novou dvojici veřejná/soukromá část nechráněná heslem?" +"Chcete-li povolit přihlášení bez hesla, můžete zkopírovat veřejný SSH klíč " +"na vzdálený stroj." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "Soubor „%(file)s“ se soukromou částí klíče neexistuje." +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Pokračovat kopírováním SSH klíče?" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -"Přejete si zkopírovat veřejnou část vašeho SSH klíče\n" -"na protější stroj a umožnit tak přihlašování bez hesla?" +"Veřejný SSH klíč nebylo možné zkopírovat. To může být kvůli problému s " +"připojením nebo oprávněním." + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Nepodvrženost stroje „{host}“ se nepodařilo ověřit." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "Otisk {keytype} klíče je:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "Zkontrolujte tento otisk! Přidat ho do souboru „known_hosts“?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Opravdu změnit složku se zálohou?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Vybraný popis cíle zálohování není prázdný." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Pokud chcete použít šifrování, je třeba, aby (složka) byla prázdná." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Neplatný soubor: Není soukromým SSH klíčem" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -"Pravost stroje „%(host)s“ se nepodařilo ověřit.\n" -"\n" -"%(keytype)s otisk klíče je:" +"Vybraný soubor ({path}) je veřejný SSH klíč. Vyberte namísto něho " +"odpovídající soukromý klíč (bez přípony „.pub“)." -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" -"Zkontrolujte tento otisk! Přejete si ho přidat do souboru „known_hosts“?" +"Soubor {path} už existuje. Nebylo možné vytvořit nový SSH klíč s takovým " +"názvem." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Zástupné vyjádření vynechaného" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "V umístění {path} se nepodařilo vytvořit nový SSH klíč." + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Zahrnout soubory a složky" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." +msgstr "" +"„{path}“ je symbolický odkaz. Odkazovaný cíl nebude zazálohován, dokud jeho " +"umístění také výslovně nezahrnete." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Zahrnout namísto toho cíl symbolického odkazu?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Zahrnout soubory" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Zahrnout složky" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "" +"Zobrazovat oznámení (prostřednictvím centra oznámení desktopového prostředí)" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Nezálohovat při provozu na akumulátor" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Údaje o stavu napájení nejsou ze systému k dispozici" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Pořizovat pouze jednu zálohu naráz" + +#: qt/manageprofiles/tab_options.py:56 +msgid "" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." +msgstr "" +"Pořizování ostatních záloh bude blokováno do dokončení té právě probíhající." +" Toto je globální předvolba, takže se bude týkat všech profilů tohoto " +"uživatele. Zapíná se ale pro každého uživatele zvlášť." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Zazálohovat soubory, které by byly při procesu obnovování nahrazeny" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Pokračovat i v případě výskytu chyb (zachovat i neúplné zálohy)" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Vynechat soubor" +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "" +"Zda se soubory, určené k zálohování změnily zjišťovat pomocí jejich " +"kontrolního součtu (přesnější)" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Vynechat složku" +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Vytvořit novou zálohu i v případě, že nedošlo k žádným změnám." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Zahrnout soubor" +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Varovat pokud volný prostor na disku poklesne pod" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_options.py:101 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" -"„%s“ je symbolický odkaz. Odkazovaný cíl nebude zazálohován, pokud jeho " -"umístění výslovně nezahrnete.\n" -"Zahrnout tedy cíl symbolického odkazu namísto odkazu samotného?" +"Zobrazí varování pokud volný prostor na disku, na který je směrována záloha," +" je menší než zadaná hodnota." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Zahrnout složku" +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." +msgstr "" +"Pokud je zapnuto pravidlo pro „Odstraňování a uchovávání“ a staré zálohy " +"jsou odebírány na základě dostupného volného místa, tato hodnota nemůže být " +"nižší než hodnota nastavená v pravidle." -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Opravdu změnit složku se zachycenými stavy?" +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Podrobnost zaznamenávání událostí (log):" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "V umístění %(path)s se nepodařilo vytvořit nový SSH klíč" +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nic" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "zapnuto" +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "uživatelská příručka" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "vypnuto" +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "" +"Následující pravidla jsou zpracovávána od začátku seznamu směrem k jeho " +"konci. Pozdější pravidla přebíjí ta dřívější. Ohledně podrobností a ukázek " +"viz {manual_link}." + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Otevřít uživatelskou příručku v prohlížeči." + +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Ponechávat nejnovější zálohu." + +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Nejnovější záloha je uchovávána v každém případě." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Toto chování není možné změnit." + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Neodstraňovat ty zálohy, které uživatel nějak nazval." + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." +msgstr "" +"Zálohy, kterým byl dán název (nad rámec obvyklého časového razítka), budou " +"za všech okolností zachovávány a nebudou odebrány." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Let" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Odstranit zálohy starší než" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Celé dny. Stávající den je ignorován." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" +"Týdny v kalendáři s pondělím coby prvním dnem. Stávající týden je ignorován." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12 měsíční periody. Stávající měsíc je ignorován." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Zásada uchovávání" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Na vzdáleném stroji spustit na pozadí." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Zásada uchovávání bude spuštěna přímo na vzdáleném stroji, nikoli lokálně. " +"Na vzdáleném stroji je třeba, aby byly nainstalovány a k dispozici příkazy " +"„bash“, „screen“ a „flock“." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Pokud vybráno, Back In Time nejprve vyzkouší vzdálený stroj." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dny jsou počítány ode dneška." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "Načíst dřívější nastavení" - -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr " a přidejte svůj uživatelský účet do systémové skupiny fuse" - -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" -"Přejděte k zachycenému stavu, ze kterého chcete obnovit nastavení " -"%(appName)s. Umístění může vypadat nějak takto: \n" -"%(samplePath)s\n" -"\n" -"Pokud se zachycené stavy nacházejí na síťovém úložišti nebo jsou šifrované, " -"bude třeba toto umístění nejprve ručně připojit. V případě, že jsou zálohy " -"ukládány na vzdálený stroj prostřednictvím SSH tunelu, je také třeba " -"zajistit přihlašování k tomuto stroji%(addFuse)s pomocí klíče.\n" -"Další pokyny naleznete v manuálové stránce aplikace (man backintime)." - -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "Nebylo nalezeno žádné nastavení" - -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "" -"Skript s uživatelsky definovanými akcemi postrádá řádek určující jeho " -"interpret (#!/bin/sh)." - -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "" -"Soubor se skriptem uživatelsky definovaných akcí nemá nastavený příznak " -"spustitelné." - -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Předvolby porovnávání (diff)" - -#: ../../qt/snapshotsdialog.py:60 +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Ponechat všechny zálohy za uplynulých" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "dnů." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Ponechat poslední zálohu z každého dne za uplynulých" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "Týdny jsou počítány od toho stávajícího. Týdny začínají v pondělí." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Ponechat poslední zálohu z každého týdne za uplynulých" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "týdnů." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Měsíce jsou počítány jako kalendářní a začíná se stávajícím měsícem." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Ponechat poslední zálohu z každého měsíce za uplynulých" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "měsíců." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Roky jsou počítány jako kalendářní a začíná se stávajícím rokem." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Ponechat poslední zálohu z každého roku za" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "za všechna leta." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… pokud volného místa zbývá méně než" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "" +"… pokud z počtu složek a souborů, které lze na souborovém systému vytvořit " +"(inode), zbývá méně než" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Odebrat nejstarší zálohu pokud…" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "" +"Pokud chcete Back In Time spustit jako správce systému (root), je zapotřebí " +"se příslušně ověřit." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" +"Pro plánování záloh spouštěných připojeními externího úložného zařízení je " +"třeba se ověřit." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Zkratky" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Místa" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Souborový systém" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Složky zálohy" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: „{profile_name}“" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: „{profile_name}“ (od uživatele „{desktop_user}“)" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Zobrazit záznam nedávných událostí" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Spustit {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Zpracování…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "manuálová stránka: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Naimportovat nastavení" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Nevybrána žádná složka" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Naimportovat" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Hledání…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" +"Vyberte složku se zálohami ze které má být naimportován soubor s " +"nastaveními. Popis umístění může vypadat jako: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" +"Pokud se složka nachází na externí nebo síťové jednotce – je třeba ji předem" +" ručně připojit." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Proskenovat znovu" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Zobrazovat skryté složky" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Zahrnout/skrýt skryté složky (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "V této složce nenalezena žádná nastavení" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Hledání dokončeno." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Zobrazit kompletní záznam událostí" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Odpočet do vypnutí" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Záloha dokončena." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Zrušit vypínání" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Vypnout nyní" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Systém bude vypnut za {n} sekundu." +msgstr[1] "Systém bude vypnut za {n} sekundy." +msgstr[2] "Systém bude vypnut za {n} sekund." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Předvolby pro porovnávání záloh" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Příkaz:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametry:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "Jako parametry umístění použít %1 a %2" +msgstr "Jako parametry umístění použijte %1 a %2" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Nastavte příkaz pro zjišťování rozdílů nebo klikněte na Storno." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Příkaz „{cmd}“ se nepodařilo v systému najít. Zkuste něco jiného nebo " +"klikněte na Storno." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Vypsat pouze odlišné zachycené stavy" +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" +"Nenastaveny žádné parametry pro příkaz diff. Náhradně bude použita výchozí " +"hodnota „{params}“." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Zálohy" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Pouze odlišující se zálohy" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "Vypsat pouze zachycené stavy, shodné s: " +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Vypsat pouze zálohy, shodné s:" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Hloubková kontrola (přesnější, ale pomalejší)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "Smazat" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" msgstr "Vybrat vše" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" msgstr "Porovnat" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Přejít na" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Porovnávat zachycený stav s ním samotným nedává smysl" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Předvolby" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Příkaz nebyl nalezen: %s" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Není možné porovnat zálohu samu se sebou – takové porovnání by bylo " +"zbytečné." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Opravdu vymazat {file_or_dir} v záloze {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Opravdu vymazat {file_or_dir} v {count} zálohách?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "VAROVÁNí: Toto nelze vzít zpět." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Vynechat {path} z budoucích záloh?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Režim správce" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -"Opravdu chcete vymazat „%(file)s“ ze zachyceného stavu „%(snapshot_id)s?“\n" +"Back In Time je v tuto chvíli spuštěné s oprávněními správce systému (root)," +" tedy s přístupem kamkoli v systému" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" -msgstr "Opravdu chcete vymazat „%(file)s“ z %(count)d zachycených stavů?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Dnes" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Včera" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "VAROVÁNÍ: Tuto operaci nelze vzít zpět!" +#: qt/timeline.py:76 +msgid "This week" +msgstr "Tento týden" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Minulý týden" + +#: qt/timeline.py:88 +msgid "This month" +msgstr "" + +#: qt/timeline.py:95 +msgid "Last month" +msgstr "" + +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Minulá kontrola {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "Vynechat „%s“ z budoucích zachycených stavů?" +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Toto NENÍ zachycený stav, ale pohled přímo do stávajících souborů na tomto " +#~ "počítači." diff --git a/common/po/da.po b/common/po/da.po index e14e4df69..02ec0799a 100644 --- a/common/po/da.po +++ b/common/po/da.po @@ -1,532 +1,381 @@ -# Danish translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Adam Sjøgren # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:25+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Danish \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-01-28 11:56+0000\n" +"Last-Translator: asjo \n" +"Language-Team: Danish \n" +"Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Profil \"%s\" findes allerede !" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Du kan ikke fjerne den sidste profil !" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Deaktiveret" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Ved hver opstart/genstart" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Hver 5 minutter" - -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Hvert 10. minut" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Hvert 30. minut" - -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Hver time" - -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Hver 2. time" - -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Hver 4. time" - -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Hver 6. time" - -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Hver 12. time" - -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Angiv interval" - -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Hver dag" - -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"Alle licenser relevante for dette projekt findes i {dir_link}-mappen. For at" +" udtrække licens- og ophavsretsinformation i SPDX metadata format per fil, " +"se {readme_link}." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Når drev bliver tilsluttet (udev)" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Hver uge" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Hver måned" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Dag(e)" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Uge(r)" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "År" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Advarsel" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Hovedprofil" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Lokal (EncFS krypteret)" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " EXPERIMENTAL!" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS krypteret)" -#: ../../common/config.py:129 +#: common/config.py:237 msgid "Local" msgstr "Lokal" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" - -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "SSH privat nøgle" - -#: ../../common/config.py:131 +#: common/config.py:240 msgid "Local encrypted" -msgstr "Lokal krypteret" +msgstr "Lokalt krypteret" -#: ../../common/config.py:131 ../../common/config.py:132 +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 msgid "Encryption" msgstr "Kryptering" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH krypteret" - -#: ../../common/config.py:135 -msgid "Default" -msgstr "Standard" - -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" - -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" - -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" - -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" - -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" - -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" - -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" - -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" - -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" - -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" - -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" - -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" - -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "hovedprofil" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil: \"%s\"" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Snapshots mappe er ikke gyldig !" - -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Du skal vælge mindst én mappe, der skal sikkerhedskopieres !" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Du kan ikke inkludere sikkerhedskopierings-mappen !" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Privat SSH nøgle" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Du kan ikke inkludere en sikkerhedskopierings-mappe !" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil: \"{name}\"" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s er ikke en mappe !" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Tilstandsbillede-mappe er ugyldig." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Mindst en mappe skal vælges til backup." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Kan ikke skrive til: %s\n" -"Er du sikker på, du har skrive-rettigheder ?" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Mappe: {path}" -#: ../../common/config.py:402 -#, python-format +#: common/config.py:332 common/config.py:347 msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Denne mappe kan ikke inkluderes i sikkerhedskopien, da den er del af " +"sikkerhedskopiens destination." -#: ../../common/config.py:407 -#, python-format +#: common/config.py:366 +#, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" - -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"Værdien for \"Slet ældste tilstandsbillede hvis ledig plads er mindre end\" " +"({val_one}) skal være mindre end eller lig med grænsen for \"Advar hvis " +"ledig plads falder under\" ({val_two})." -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Avancerede indstillinger" - -#: ../../common/config.py:413 -#, python-format +#: common/config.py:371 msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" +"Juster venligst indstillingen så tilstandsbilledesletningsgrænsen ikke er " +"højere end advarselsgrænsen." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Kan ikke finde crontab.\n" -"Er du sikker på, at cron er installeret ?\n" -"Hvis ikke, skal du deaktivere alle automatiske sikkerhedskopieringer." +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Udev-planlægning virker ikke med indstilling {mode}" -#: ../../common/config.py:1478 +#: common/config.py:1569 msgid "Failed to write new crontab." -msgstr "" - -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Skedulere udev virker ikke med indstilling %s" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Fandt ikke UUID for \"%s\"" - -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" -"Kan ikke tilslutte '%(command)s':\n" -"\n" -"%(error)s" +msgstr "Kunne ikke skrive ny crontab." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "Konfig for krypteret mappe ikke fundet." - -#: ../../common/encfstools.py:136 +#: common/config.py:1577 msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Lav en ny krypteret mappe?" - -#: ../../common/encfstools.py:137 +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron kører ikke, selvom crontab-kommandoen er tilgængelig. Planlagte backup-" +"job vil ikke blive kørt. Cron kan være installeret, men ikke aktiveret. Prøv" +" kommandoerne “systemctl enable cron” og \"systemctl start cron\" eller " +"kontakt hjælpekanalerne for din GNU/Linux-distribution for hjælp." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Kunne ikke gemme konfiguration" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Kunne ikke læse konfiguration" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Profil \"{name}\" findes allerede." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Den sidste profil kan ikke slettes." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Kan ikke tilslutte \"{command}\"" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Opsætning for krypteret mappe ikke fundet." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Lav en ny krypteret mappe?" + +#: common/encfstools.py:204 msgid "Cancel" msgstr "Annullér" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Bekræft venligst kodeord" - -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Kodeord passer ikke" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Genindtast venligst EncFS-kodeordet for at bekræfte." -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" -"encfs version 1.7.2 og tidligere har en fejl ved brug af option --reverse. " -"Opdatér venligst encfs" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFS-kodeordet passer ikke." -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 +#: common/encfstools.py:659 common/snapshots.py:1128 msgid "Take snapshot" msgstr "Tag et tilstands-billede" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" -"Hash kollision skete i hash_id %s. Forøg den globale værdi hash_collision og " -"forsøg igen." - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Ude af stand til at initialisere krypteret sti \"{command}\"" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Kan ikke fjerne {mountprocess} fra {mountpoint}." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" -"%(user)s er ikke medlem af gruppe 'fuse'.\n" -" Kør 'sudo adduser %(user)s fuse'. Bekræft ændringerne ved at logge ud og " -"ind igen.\n" -"Se 'man backintime' for yderlig information." +"{command} ikke fundet. Installér venligst (f.eks. med \"{installcommand}\")" -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "tilsluttelsespunkt %s ikke tomt." +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Tilsluttelsespunkt {mntpoint} er ikke tomt." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "Lås af tilslutningsproces udløbet" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Indtast adgangskode for {mode} profil \"{profile}\":" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Profil '%(profile)s': Indtast kodeord for %(mode)s: " - -#: ../../common/snapshotlog.py:62 +#: common/schedule.py:238 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" -"### Denne log er blevet afkodet med automatisk søge mønster\n" -"### Hvis nogle stier ikke er afkodet kan du manuelt afkode dem med:\n" +"Kunne ikke installere Udev regel for profil {profile_id}. DBus Service " +"'{dbus_interface}' var ikke tilgængelig." + +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Fandt ikke UUID for {path}" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 +#: common/snapshots.py:378 common/snapshots.py:656 msgid "FAILED" msgstr "MISLYKKEDES" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Gendan tilladelser:" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Gendan tilladelser" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 msgid "Done" msgstr "Færdig" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" - -#: ../../common/snapshots.py:669 +#: common/snapshots.py:749 msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" msgstr "" -"Kan ikke finde tilstands-billede-mappen.\n" -"Hvis den er på et flytbart drev indsæt venligst dette." +"De følgende indgang i inkluderingslisten har ikke nogen tilsvarende fil " +"eller mappe i tilstandsbillede-kilden:" + +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Venter med backup under batteridrift" -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Venter %s sekund." -msgstr[1] "Venter %s sekunder." +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Kan ikke finde tilstandsbillede-mappen." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Kunne ikke tage tilstands-billede %s !!!" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Hvis den er på et flytbar drev, tilslut venligst drevet." -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Afslutter" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Venter {n} sekund." +msgstr[1] "Venter {n} sekunder." -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Kan ikke oprette mappen: %s" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Kunne ikke tage tilstandsbillede {snapshot_id}." -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Gem config fil ..." +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Hav venligst tålmodighed. Færdigører…" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Gem tilladelse ..." +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Kan ikke oprette mappe." -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Gemmer konfigurationsfil…" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Gemmer tilladelser…" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." msgstr "" +"Fandt efterladt tilstandsbillede {snapshot_id} som kan fortsættes med." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Kan ikke fjerne mappen: %s" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Sletter efterladt {snapshot_id}-mappe fra seneste kørsel" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Tag et tilstands-billede" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Kan ikke fjerne mappe" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Opretter tilstandsbillede" + +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Succes" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Delvist overført på grund af fejl" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" +"Delvis overførsel da nogle filer fra kilden er forduftet (se 'man rsync')" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Kan ikke omdøbe %(new_path)s til %(path)s" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' afsluttede med slutværdien {exit_code}" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Smart fjern" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Kig i 'man rsync' for flere detaljer" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Fjern gamle tilstands-billeder" +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "Negative rsync slutværdier er signalnumre, se 'kill -l' og 'man kill'" + +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Ingen ændringer, så et nyt tilstandsbillede er ikke nødvendigt" + +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Kan ikke omdøbe {new_path} til {path}." + +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Bruger regler til at fjerne gamle tilstandsbilleder" -#: ../../common/snapshots.py:1427 +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Bruger bevaringspolitik" + +#: common/snapshots.py:2041 msgid "Trying to keep min free space" msgstr "Prøv at holde minimum fri plads" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Prøv at beholde minimum %d%% inoder fri" - -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "MED FEJL!" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Prøver at holde mindst {perc} inoder fri" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 msgid "Now" msgstr "Nu" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Kan ikke tilslutte %s" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Kan ikke tilslutte {sshfs}" -#: ../../common/sshtools.py:315 +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "Kan ikke finde ssh-agent. Sørg venligst for at den er installeret." + +#: common/sshtools.py:489 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." @@ -534,1143 +383,2202 @@ msgstr "" "Kunne ikke fjerne låsen på ssh privat nøgle. Forkert kodeord elle kodeordet " "er ikke tilgængelig for cron." -#: ../../common/sshtools.py:345 -#, python-format +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Stien findes i den anden ende, men er ikke en mappe." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Stien i den anden ende er ikke skrivbar." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Stien i den anden ende er ikke eksekverbar." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Kan ikke oprette fjernmappe." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Den anden maskine {host} understøtter ikke {command}" + +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Check-kommandoerne på vært {host} gav en ukendt fejl" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Den anden maskine {host} understøtter ikke hardlinks" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Kopiér offentlig ssh-nøgle \"{pubkey}\" til vært \"{host}\"." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Indtast venligst et kodeord for \"{user}\"." + +#: common/tools.py:382 +#, python-brace-format msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"Kodeløs login for %(user)s@%(host)s fejlede. Se'man backintime' for yderlig " -"informationer." +"Destinationsfilsystem for {path} er formatteret med NTFS, som ikke er fuldt " +"kompatibelt med UNIX-lignede filsystemer." -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} er ikke en gyldig mappe." -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Oprettelse af følgende mappe fejlede:" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Skriveadgang kan være begrænset." -#: ../../common/sshtools.py:470 -#, python-format +#: common/tools.py:471 +#, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"Destinationsfilsystem for {path} er formatteret med FAT, som ikke kan " +"håndtere hard-links. Brug venligst et understøttet GNU/Linux-filsystem." -#: ../../common/sshtools.py:472 -#, python-format +#: common/tools.py:482 +#, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"Destinationsfilsystem for {path} er delt via SMB. Undersøg venligst om SMB " +"serveren understøtter symlinks eller aktiver {copyLinks} i {expertOptions}." + +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Kopier links (følg symbolske links)" + +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Avancerede indstillinger" -#: ../../common/sshtools.py:474 -#, python-format +#: common/tools.py:491 +#, python-brace-format msgid "" -"Couldn't create remote path:\n" -" %s" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"Destinationsfilsystemet for {path} er delt via ssfhs. Sshfs understøtter " +"ikke hardlinks. Benyt venligst 'SSH' mode i stedet." + +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Filoprettelse fejlede i mappe:" + +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Om Back In &Time" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Ophavsret:" + +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Forfattere:" + +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Oversættere:" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Adam Sjøgren " + +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Oversætterinformation er ikke tilgængelig for det nuværende sprog." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "dette link" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "Følg {thislink} for at se oversætterinformation for alle sprog." + +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Projektets hjemmeside" + +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Brugervejledning" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Åbn brugervejledning i browser (lokal hvis tilgængelig, ellers på nettet)" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Version{BOLDEND}: {version}" + +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"{app_name} ser ud til at køre for første gang, eftersom der ingen " +"konfiguration var." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/app.py:337 msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Importer en eksisterende konfiguration fra en tilstandsbillede-" +"destinationsmappe eller anden computer?" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Tryk derefter OK." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Opret et tilstandsbillede" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Brug ændringstid og størrelse til at opdage filændringer." -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Opret et tilstandsbillede (checksum modus)" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Brug checksummer til at opdage filændringer." -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Sæt tilstandsafbildning på pause" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Genoptag tilstandsafbildning" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Stop tilstandsafbildning" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Tilstands-billede-navn" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Opdatér tilstandsbillede-liste" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Fjern tilstands-billede" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Navngiv tilstandsbillede" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Fjern tilstandsbillede" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Åben tilstandsbillede-log" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Indstillinger" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Se log for det valgte tilstandsbillede." + +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Åben seneste tilstandsbillede-log" + +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Se log fra det seneste tilstandsbillede." + +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "hovedprofil…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Redigér bruger-callback" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" -msgstr "" +msgstr "Luk ned" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Luk ned når tilstandsbilledet er færdiggjort." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Sæt sprog op…" + +#: qt/app.py:540 msgid "Exit" msgstr "Afslut" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Hjælp" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man-side: Back In Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Viser man-side om Back In Time (backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Websted" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man-side: Profilkonfigurationsfil" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "Viser man-side om profilkonfigurationsfil (backintime-config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Åbn Back In Time hjemmeside i browser" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" +msgstr "Ændringslog" + +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" +"Åbn listen over ændringer i weblæser (lokal hvis tilgængelig, ellers på " +"nettet)" -#: ../../qt/app.py:167 +#: qt/app.py:573 msgid "FAQ" -msgstr "" +msgstr "OSS" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Åbn Ofte Stillede Spørgsmål (OSS) i browser" + +#: qt/app.py:577 msgid "Ask a question" -msgstr "" +msgstr "Stil et spørgsmål" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Rapporter en fejl" -#: ../../qt/app.py:174 ../../qt/app.py:1439 +#: qt/app.py:584 +msgid "Translation" +msgstr "Oversættelse" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Vis beskeden om deltagelse i oversættelse igen." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Krypteringsovergang (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Vis besked fjernelse af EncFS igen." + +#: qt/app.py:599 msgid "About" msgstr "Om" -#: ../../qt/app.py:204 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Gendan" + +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "Gendan de valgte filer eller mapper på det oprindelige sted." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Gendan …" + +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "Gendan de valgte filer eller mapper på et nyt sted." + +#: qt/app.py:615 +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "Gendan den viste mappe og al dens indhold på det oprindelige sted." + +#: qt/app.py:621 +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "Gendan den viste mappe og al dens indhold på et nyt sted." + +#: qt/app.py:624 msgid "Up" msgstr "Op" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" msgstr "Vis skjulte filer" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Gendan" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Sammenlign tilstandsbilleder…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "kandidat til udgivelse" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Vis beskeden om denne udgivelseskandidat igen." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Sikkerhedskopiér" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Gendan" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Hjælp" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Tilstamds-billeder" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Statusikon" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automatisk" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:784 +msgid "Light icon" +msgstr "Lyst ikon" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Genveje" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Mørkt ikon" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Alene ikoner" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:811 +msgid "Text only" +msgstr "Alene tekst" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Tekst under ikoner" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Tekst ved siden af ikon" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Denne mappe findes ikke\n" +"i det valgte tilstandsbillede." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Kan ikke finde tilstands-billede-mappen.\n" -"Hvis den er på et flytbar drev, indsæt venligst drevet og tryk OK" +"Denne mappe findes ikke\n" +"i det valgte tilstandsbillede." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Hvis du lukker dette vindue så kan Back In Time ikke lukke din maskine ned " +"når tilstandsbilledet er færdigt." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "Arbejder:" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Luk vinduet alligevel?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" msgstr "Færdig, ingen sikkerhedskopiering nødvendig" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Fejl:" +#: qt/app.py:1260 +msgid "Working:" +msgstr "Arbejder:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Arbejder" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Fejl" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "Sendt:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "" +msgstr "Hastighed:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Globalt" +msgstr "ETA:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Root" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "&Sikkerhedskopier:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Hjemmemappe" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Gendan {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Sikkerhedskopi-mapper" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Gendan {path} …" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Er du sikker på du ønsker at fjerne tilstands-billedet:\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Hej\n" +"Du har nu brugt Back In Time på {language} et par gange nu.\n" +"Oversættelsen af din version af Back In Time i {language} er {perc} færdig oversat. Ligegyldigt af niveau kan du hjælpe med oversættelsen og derved Back In Time.\n" +"Besøg venligst {translation_platform_url} hvis du ønsker at kontribuere. For yderlige hjælp eller spørgsmål, besøg venligst {back_in_time_project_website}.\n" +"Vi undskylder forstyrrelsen, denne besked vil ikke blive vist igen. Denne dialog box er tilgængelig til enhver tid i 'hjælp' menuen.\n" +"Dit Back In Time Team" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "oversættelses platform" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Websted" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Din oversættelse" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "I fødiverset på Mastodon: {link_and_label}." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Email til {link_and_label}." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Emailliste {link_and_label}." -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} på projektets hjemmeside." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Opret en fejlrapport" -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Eller du kan benytte en anden mulighed du foretrækker." + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Denne udgave af Back In Time er en udgivelseskandidat og er primært tiltænkt stabilitetstest som forberedelse til næste udgivelse.\n" +"Ingen brugerdata eller telemetri opsamles. Men, Back In Time-holdet er meget interesseret i at vide om udgivelseskandidaten bliver brugt og om det er værd at fortsætte med at stille sådanne før-udgivelses versioner til rådighed.\n" +"Derfor beder holdet dig om kort at tilkendegive om du har prøvet denne udgave, også hvis du ikke oplevede nogle problemer. Selv en hurtig testkørsel på et par minutter hjælper os.\n" +"De følgende kontaktmåder findes:\n" +"{contact_list}\n" +"I denne udgave vises denne besked ikke igen, men du kan altid se den igen i hjælpemenuen.\n" +"Tak for din støtte og for at hjælpe os med at gøre Back In Time bedre!\n" +"Dit Back In Time-hold" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Kun {free} plads fri på målet, hvilket er under den opsatte grænse på " +"{threshold}." -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Forsæt med at danne tilstandsbillede?" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Alle nyere filer i {path} vil blive slettet. Fortsæt?" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "Alle nyere filer i den oprindelige mappe vil blive slettet. Fortsæt?" -#: ../../qt/app.py:1105 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Advarsel{BOLDEND}: Hvis du sletter filer i filsystemets rod risikerer " +"du at ødelægge hele dit system." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Se nuværende disk-indhold" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Tilstandsbillede-navn" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Tilstands-billede: %s" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Slet dette tilstandsbillede?" +msgstr[1] "Slet disse tilstandsbilleder?" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Se tilstands-billedet lavet ved %s" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "Sprogopsætningen ændres først efter genstart af Back In Time." -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Versionerede tilstandsbilleder (root)" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"Brugervenligt grafisk interface for versionerede tilstandsbilleder der " +"reducerer lagerforbrug (root mode)" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Versionerede tilstandsbilleder" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Brugervenligt grafisk interface for versionerede tilstandsbilleder der " +"reducerer lagerforbrug" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Spørgsmål" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Opret sikkerhedskopier med {suffix} tilføjet før lokale elementer " +"overskrives eller fjernes." -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" +msgstr "" +"Nyere version af filer vil blive omdøbt med {suffix} tilføjet før " +"genddannelse. Hvis du ikke har brug for dem længere, kan de fjernes med " +"denne kommando:" + +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Gendan alene elementer som ikke findes eller er nyere end dem der findes. " +"Bruger \"{rsync_example}\" parameteren." + +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Fjern nyere elementer i oprindelsesmappen." -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Gendan valgte filer eller mapper på den oprindelige plads og slet filer " +"eller mapper som ikke er i tilstandsbilledet. Vær ekstremt forsigtig for " +"dette vil slette filer og mapper som blev sprunget over da tilstandsbilledet" +" blev dannet." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Er du sikker på at du vil gendanne dette element i den nye mappe?" +msgstr[1] "" +"Er du sikker på at du vil gendanne disse elementer i den nye mappe?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Er du sikker på at du vil gendanne dette element?" +msgstr[1] "Er du sikker på at du vil gendanne disse elementer?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Bruger-kode: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"Bruger-kode scriptet skal have en shebang på den første linje (for eksempel " +"{example})." + +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Vis/skjul skjulte filer og mapper (Ctrl+H)" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Tilføj til Inkludér" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Tilføj til Spring over" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Vælg fra ofte brugte elementer for at tilføje til ekskluderingsliste." +msgstr[1] "" +"Vælg fra ofte brugte elementer for at tilføje til ekskluderingsliste." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Vælg fra ofte brugte elementer for at tilføje til ekskluderingsliste." +msgstr[1] "" +"Vælg fra ofte brugte elementer for at tilføje til ekskluderingsliste." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Sæt sprog op" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Oversat: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Systemstandard" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Brug styresystemets sprog." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Tilstandsbillede-logvisning" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Se Sidste Log" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Profil:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "&Sikkerhedskopiér:" + +#: qt/logviewdialog.py:93 msgid "Filter:" -msgstr "" +msgstr "Filter:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Fejl, [I] Information, [C] Ændre" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "afkod paths" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" -msgstr "" +msgstr "Alles" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Ændringer" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Fejl" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Information" +msgstr[1] "Informationer" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync overførelsesfejl (eksperimentalt)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Håndtér profiler" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Redigér" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Tilføj" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Fjern" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Generelt" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Inkludér" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Ekskludér" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Fjer&rn & Opbevaring" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Arbejder..." +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "I&ndstillinger" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "I dag" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "A&vancerede indstillinger" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "I går" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Gendan opsætning" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Denne uge" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Ny profil" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Sidste uge" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Omdøb profil" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Er du sikker på at du vil slette profilen \"{name}\"?" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Kopiér symbolske links som filer" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Kopiér symbolske links som rigtige filer eller mapper i tilstandsbilledet. " +"Vælg om alle links eller kun dem der peger ud af kilden skal kopieres." -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Redigér" - -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Denne valgmulighed kan øge tilstandsbilledets størrelse." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Slået fra som standard." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Alle symbolske links bliver erstattet med de rigtige filer eller mapper de " +"peger på. Dette øger tilstandsbilledets størrelse og kan lagre de samme " +"filer flere gange." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Generelt" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Bruger 'rsync --copy-links'." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Kun eksterne" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"Kun links der peger ud af tilstandsbilledets kilde kopieres som filer. Dette" +" øger tilstandsbilledets størrelse og kan lagre de samme filer flere gange." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Hvor skal tilstands-billeder gemmes" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Bruger 'rsync --copy-unsafe-links'." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Editor & Office midlertidige filer" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacs backup filer" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs autogem filer" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Vim swap filer" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Office midlertidige filer" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice & andre OpenDocument programmers låsefiler" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniature- og Midlertidige Billeder" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" +"Miniaturebillede-cache på GNU/Linux og andre unix-lignende operativsystemer" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Miniaturebillededatabase på Windows" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Metadata-mappe på macOS" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Applikationsspecifikke låse" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discord applikationslåsefil" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord sessionslåsefil" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Mozilla Firefox & Thunderbird låsefil" + +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Caches & Midlertidige mapper" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Brugerapplikationscache" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Midlertidig system-mappe" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Pakke-cache for Debian(-baserede) GNU/Linux distributioner" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Flatpak applikations og kørselsmiljø lagringsplads" + +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Systemkørselsmiljømapper" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Kerne- og processinformation" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Enheds- og anden hardwareinformation (sysfs interface)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Enhedsnoder" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Systemkørselsfiler" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Andre ikke-permanente" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Liste af nuværende monterede filsystemer" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "System swap-fil (virtuel hukommelse)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "GNOME virtuelt filsystem monteringspunkt" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Gendannet filsystem objekter" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Diverse" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Metadata-mappe på Microsoft Windows" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Bruger skraldespand" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "System tilstandsafbildning filer" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Ekskludéringsforslag" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" +"Vælg ofte brugte elementer til at tilføje til tilstandsafbildning eksklusiv " +"liste." + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Standard" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Nulstil til forudinstillet valg" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Planlæg" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "" +msgstr "Dag:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "" +msgstr "Ugedag:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Tid:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" +msgstr "Timer:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "hver time" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minutter:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" +"Kør Back In Time så snart drevet tilsluttes (kun en gang hver X'te dag). Du " +"vil blive spurgt om dit sudo kodeord." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Kør Back In Time med jævne mellemrum. Dette er praktisk hvis computeren ikke" +" er tændt på faste tidspunkter." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "" +msgstr "Hver:" -#: ../../qt/settingsdialog.py:382 -msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Aktiver logning af debug beskeder" -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Inkludér" +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "Skriver debugniveau-beskeder ind i system log via \"--debug\"." -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" +#: qt/manageprofiles/schedulewidget.py:120 +msgid "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Advarsel: Brug kun dette midlertidigt til fejlfinding, da det genererer en " +"stor mængde output." -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Tilføj fil" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Deaktiveret" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Tilføj mappe" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Ved hver opstart/genstart" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Ekskludér" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Hvert minut" +msgstr[1] "Hvert {n}. minut" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Hver time" +msgstr[1] "Hver {n} timer" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Angiv interval" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Hver dag" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Gentag (anacron)" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Når drev bliver tilsluttet (udev)" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Hver uge" -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Hver måned" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Auto-fjern" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Hvert år" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Ældre end:" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Time(r)" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Hvis fri plads er mindre end:" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Dag(e)" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Uge(r)" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Måned(er)" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Tilpassede timer kan kun være en kommasepareret liste af timer (for eksempel" +" 8,12,18,23) eller */3 for periodisk sikkerhedskopiering hver tredje time." -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "Monat(e)" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "" +"Wählt eine vorhandene private Schlüsseldatei von einem anderen Ort aus." -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "Einen Schnappschuss pro Jahr behalten, für alle Jahre" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Benannte Schnappschüsse nicht entfernen" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Erstellt einen neuen SSH-Schlüssel ohne Passphrase." -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Optionen" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Vollständiger Pfad: {path}" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Benachrichtigungen aktivieren" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Privater Schlüssel:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Schnappschüsse im Akkubetrieb deaktivieren" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "SSH-Konfiguration des Systems verwenden" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Energiestatus des Systems nicht verfügbar." +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" +"Wählt keine Schlüsseldatei. SSH Verbindungen nutzen die Konfiguration des " +"SSH-Clients aus dem System (z.B., ~/.ssh/config)." + +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH-Proxy" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "Nur einen Schnappschuss zur selben Zeit ausführen" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Host:" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" + +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "User:" + +#: qt/manageprofiles/sshproxywidget.py:71 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" -"Andere Schnappschüsse werden blockiert, bis der aktuelle Schnappschuss " -"fertiggestellt ist.\n" -"Das ist eine globale Option, die alle Profile dieses Benutzers betreffen " -"wird.\n" -"Die Option muss aber auch für alle anderen Benutzer aktiviert werden." +"Verbindet sich mit dem Ziel-Host über diesen Proxy (auch bekannt als Jump-" +"Host). Für Details siehe \"-J\" in der Dokumentation des \"ssh\"-Befehls " +"oder \"ProxyJump\" in der \"ssh_config\" man page." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "Beim Wiederherstellen Dateien ersetzen" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Info{ENDBOLD}: Im Modus 'SSH verschlüsselt', funktionieren nur " +"einzelne oder doppelte Sternchen (z. B. {example2}). Andere Arten von " +"Platzhaltern und Mustern werden ignoriert (z. B. {example1}). Aufgrund der " +"Verschlüsselung durch EncFS, sind Dateinamen in diesem Modus nicht " +"vorhersehbar." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Muster, Dateien oder Verzeichnisse ausschließen" + +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Muster hinzufügen" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Dateien hinzufügen" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Verzeichnisse hinzufügen" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Vorschläge" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "Häufig genutzte Elemente für die Ausschlussliste auswählen." + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Dateien ausschließen, die größer sind als:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Dateien ausschließen, die größer sind als {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Wenn der „Vollständige rsync-Modus“ deaktiviert ist, wirkt sich diese " +"Einstellung nur auf neu erstellte Dateien aus, da rsync sie als " +"Übertragungsoption und nicht als Ausschlussregel behandelt. Folglich bleiben" +" große Dateien, die bereits gesichert wurden, auch dann in den Sicherungen " +"erhalten, wenn sie modifiziert werden." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Ausschlussmuster" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Bei Fehlern fortfahren (unvollständige Schnappschüsse behalten)" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Ausschlussmuster eingeben:" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "Neuen Schnappschuss unabhängig von Änderungen erstellen." +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Für Hilfe siehe den Abschnitt {link} der rsync-Manpage." -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Protokollierungsstufe:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Öffnet die rsync-Manpage" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Kein(e)" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Dateien ausschließen" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Änderungen & Fehler" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Verzeichnisse ausschließen" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Ändern Sie diese Optionen nur, wenn Sie sich wirklich sicher sind!" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" +"Deaktiviert, da dieses Muster im Modus »SSH verschlüsselt« nicht " +"funktionsfähig ist." + +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"Diese Optionen dienen der fortgeschrittenen Konfiguration. Nur ändern, wenn " +"die Auswirkungen vollständig bekannt sind." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "»nice« starten:" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Starte »rsync« mit »{cmd}«:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" msgstr "als cron-job" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" msgstr "auf entfernten Rechnern" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "»ionice« starten:" - -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "wenn ein manueller Schnappschuss erstellt wird" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "wenn eine Sicherung manuell erstellt wird" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "»rsync« mit »nocache« starten:" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Bitte 'nocache' installieren, um diese Option freizuschalten." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" msgstr "auf dem lokalen Rechner" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "stdout in cronjobs nach /dev/null umleiten." +msgstr "Leitet stdout in cronjobs nach /dev/null um." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" +"Wenn ein MTA installiert ist, sendet Cron eine automatische E-Mail mit dem " +"angehängten Ausgabeprotokoll der Cronjobs." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "stderr in cronjobs nach /dev/null umleiten." +msgstr "Leitet stderr in cronjobs nach /dev/null um." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "rsync-Bandbreite drosseln: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" +"Wenn ein MTA installiert ist, sendet Cron eine automatische E-Mail mit dem " +"angehängten Fehlerprotokoll der Cronjobs." + +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/s" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr " kB/s" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Bandbreitennutzung von rsync begrenzen:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "ACLs bewahren" +msgstr "ACL bewahren" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Erweiterte Attribute (xattr) bewahren" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" -"Unsichere Verknüpfungen kopieren (funktioniert nur mit absoluten " -"Verknüpfungen)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Auf ein Dateisystem beschränken" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Optionen müssen in Anführungszeichen stehen z.B.: {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "Weitere Optionen zu rsync hinzufügen" +msgstr "Zusätzliche Optionen zu rsync hinzufügen" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Präfix, das vor jedem Befehl auf dem Remote-Host ausgeführt wird." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -"Optionen müssen mit schließendem Anführungszeichen angegeben werden z.B.: --" -"exclude-from=\"/pfad/zu/meiner_ausschlussdatei\"." +"Variablen müssen mit \\$FOO maskiert werden. Das betrifft nicht rsync. Um " +"ein Präfix für rsync hinzuzufügen, verwenden Sie »{example_value}« mit " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "Präfix zum SSH-Befehl hinzufügen" - -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" -msgstr "" -"Präfix der vor jedem Befehl auf dem Entfernten Rechner ausgeführt wird.\n" -"Variablen müssen mit \\$FOO geschützt werden.\n" -"Dies betrifft nicht rsync. Um einen Präfix für rsync hinzuzufügen\n" -"muss \"%(cbRsyncOptions)s\" mit\n" -"%(rsync_options_value)s genutzt werden.\n" -"\n" -"%(default)s: %(def_value)s" - -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 +#: qt/manageprofiles/tab_expert_options.py:293 msgid "default" msgstr "Standard" -#: ../../qt/settingsdialog.py:870 +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Präfix zu SSH-Befehl hinzufügen" + +#: qt/manageprofiles/tab_expert_options.py:308 msgid "Check if remote host is online" -msgstr "Überprüfen, ob der entfernte Rechner am Netz ist" +msgstr "Überprüfen, ob der entfernte Rechner online ist" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" -"Achtung: Wenn dies deaktiviert ist und der entfernte Rechner\n" -"nicht verfügbar ist, kann es zu einigen verwirrenden\n" +"Warnung: Wenn diese Option deaktiviert ist und der entfernte Rechner nicht " +"verfügbar ist, kann es zu einigen verwirrenden Fehlern kommen." + +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "" +"Prüfen, ob der entfernte Rechner alle erforderlichen Befehle unterstützt." + +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." +msgstr "" +"Warnung: Wenn diese Option deaktiviert ist und der entfernte Rechner nicht " +"alle erforderlichen Befehle unterstützt, kann es zu einigen verwirrenden " "Fehlern kommen." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(Standard: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "Deaktiviert" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "Aktiviert" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Modus:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Wo Sicherungen zu speichern sind" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH-Einstellungen" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Pfad:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Schlüsseldatei:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Passwort" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Passwort in Schlüsselbund speichern" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" -"Bitte prüfen, ob der entfernte Rechner alle erforderlichen Befehle " -"unterstützt" +"Passwort für Cron zwischenspeichern (Sicherheitsproblem: Systemverwalter " +"»root« kann das Passwort lesen)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Erweitert" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Vollständiger Sicherungspfad:" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" -"Achtung: Wenn dies deaktiviert ist und der entfernte Rechner\n" -"nicht alle erforderlichen Befehle unterstützt, kann es zu\n" -"einigen verwirrenden Fehlern kommen." +"Die Zeitplanung ist deaktiviert, da keine Cron-Installation gefunden wurde. " +"Bitte Cron installieren, um geplante Sicherungen zu aktivieren." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "Konfiguration wiederherstellen" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Der Pfad für das Sicherungsziel darf nicht leer sein." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "Benutzerrückruf bearbeiten" - -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" -"Die vollständige Systemsicherung kann nur auf der selben physikalischen " -"Platte, mit den selben Festplattenpartitionen, wie er von der Quelle, einen " -"Schnappschuss erstellen. Das Wiederherstellen auf einer neuen physischen " -"Festplatte oder der selben Platte mit anderen Partitionen, wird " -"möglicherweise ein defektes und unbrauchbares System erstellen.\n" -"\n" -"Die vollständige Systemsicherung wird einige Einstellungen überschreiben, " -"die angepasst wurden. Fortfahren?" - -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Neues Profil" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Das Verschlüsselungspasswort darf nicht leer sein." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Profil umbenennen" +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" +msgstr "" +"Beim Versuch, sich am Zielrechner anzumelden, ist ein Fehler aufgetreten. " +"Die folgende Fehlermeldung wurde zurückgegeben:" + +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." +msgstr "" +"Um die passwortlose Anmeldung zu ermöglichen, kann der öffentliche SSH-" +"Schlüssel auf das entfernte System kopiert werden." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Sind Sie sicher, dass Sie das Profil »%s« löschen wollen?" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Mit dem Kopieren des SSH-Schlüssels fortfahren?" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -"Benutzerdefinierte Stunden, bitte als Kommata getrennte Stundenliste " -"schreiben (z.B. 8,12,18,23) oder */3 für wiederholte Sicherungen alle 3 " -"Stunden." +"Der öffentliche SSH-Schlüssel konnte nicht kopiert werden. Dies kann auf ein" +" Verbindungs- oder Berechtigungsproblem zurückzuführen sein." + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Die Echtheit des Rechners {host} kann nicht festgestellt werden." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} Fingerabdruck des Schlüssels ist:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "" +"Bitte diesen Fingerabdruck überprüfen. Diesen zur Datei »known_hosts« " +"hinzufügen?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Wirklich das Sicherungsverzeichnis ändern?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Das ausgewählte Sicherungsziel ist nicht leer." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Es muss leer sein, um die Verschlüsselung zu verwenden." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Ungültige Datei: Der SSH Schlüssel ist nicht privat" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." +msgstr "" +"Die ausgewählte Datei {path} ist ein öffentlicher SSH-Schüssel. Bitte " +"stattdessen die entsprechende private Schlüsseldatei auswählen (ohne ».pub« " +"Endung)." + +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" +"Die Datei {path} ist bereits vorhanden. Es kann kein neuer SSH-Schüssel mit " +"diesem Namen erstellt werden." + +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Neuer SSH-Schlüssel in {path} konnte nicht erstellt werden." + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Dateien und Verzeichnisse einbeziehen" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." +msgstr "" +"»{path}« ist ein symbolischer Link (Symlink). Das verlinkte Ziel wird erst " +"gesichert, wenn es ebenfalls enthalten wird." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Stattdessen das verlinkte Ziel einbeziehen?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Dateien einbeziehen" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Verzeichnisse einbeziehen" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Benachrichtigungen aktivieren" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Sicherungen deaktivieren, wenn im Akkubetrieb" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Energiestatus des Systems nicht verfügbar" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Nur eine Sicherung gleichzeitig ausführen" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:56 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." +msgstr "" +"Andere Sicherungen werden blockiert, bis die aktuelle Sicherung " +"abgeschlossen ist. Dies ist eine globale Einstellung, die alle Profile " +"dieses Benutzers betrifft. Sie muss jedoch auch für alle anderen Benutzer " +"aktiviert werden." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Beim Wiederherstellen Dateien ersetzen" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Bei Fehlern fortfahren (unvollständige Sicherungen behalten)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Prüfsumme verwenden, um Änderungen zu erkennen" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." msgstr "" -"Sie haben keine private Schlüsseldatei für SSH ausgewählt.\n" -"Möchten Sie ein neues Öffentlich/Privates Schlüsselpaar ohne Passwort " -"erstellen?" +"Neue Sicherung erstellen, egal ob Änderungen vorgenommen wurden oder nicht." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "Die private Schlüsseldatei »%(file)s« ist nicht vorhanden." +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Warne, wenn freier Speicherplatz weniger als" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" -"Möchten Sie Ihren öffentlichen SSH-Schlüssel\n" -"auf den entfernten Rechner kopieren,\n" -"um Anmeldung ohne Passwort zu aktivieren?" +"Zeigt eine Warnung, wenn der freie Speicherplatz am Sicherungsziel geringer " +"ist, als der angegebene Wert." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -"Die Echtheit des Rechners »%(host)s« kann nicht festgestellt werden.\n" -"\n" -"%(keytype)s Schlüsselfingerabdruck ist:" +"Wenn die Richtlinien zum Löschen & Aufbewahren aktiviert sind und alte " +"Sicherungen abhängig vom verfügbaren Speicherplatz gelöscht werden, darf " +"dieser Wert nicht niedriger sein als der in der Richtlinie festgelegte Wert." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Protokollierungsstufe:" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nichts" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "Benutzerhandbuch" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" -"Bitte überprüfen Sie diesen Fingerabdruck! Möchten Sie ihn zu Ihrer " -"known_hosts-Datei hinzufügen?" +"Die folgenden Regeln werden von oben nach unten verarbeitet. Spätere Regeln " +"setzen frühere Regeln außer Kraft. Siehe das {manual_link} für Details und " +"Beispiele." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Ausschlussmuster" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Öffnet Benutzerhandbuch im Browser." + +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Die aktuellste Sicherung behalten." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Datei ausschließen" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Die neueste Sicherung wird unter allen Umständen aufbewahrt." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Ordner ausschließen" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Das Verhalten kann nicht geändert werden." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Datei einbeziehen" +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Benannte Sicherungen behalten." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" -"»%s« ist eine symbolische Verknüpfung. Das verknüpfte Ziel wird nicht mit " -"gesichert, wenn Sie es nicht auch hinzufügen.\n" -"Möchten Sie das Ziel der Verknüpfung stattdessen hinzufügen?" +"Sicherungen, denen zusätzlich zum üblichen Zeitstempel ein Name zugewiesen " +"wurde, werden unter allen Umständen bewahrt und nicht entfernt." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Ordner einbeziehen" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Jahr(e)" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Sind Sie sicher, dass Sie den Schnappschussordner ändern wollen?" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Entferne Sicherungen, die älter sind als" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "Erstellen eines neuen SSH-Schlüssels in %(path)s ist fehlgeschlagen" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Vollständige Tage. Aktueller Tag wird ignoriert." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "Aktiviert" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "Kalenderwochen beginnend mit Montag. Aktuelle Woche wird ignoriert." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "Deaktiviert" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12 Monats Periode. Aktueller Monat wird ignoriert." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Aufbewahrungs-Richtlinien" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Auf entferntem Rechner im Hintergrund ausführen." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Die Aufbewahrungs-Richtlinien werden direkt auf dem entfernten System " +"ausgeführt, nicht lokal. Die Befehle „bash“, „screen“ und „flock“ müssen auf" +" dem entfernten System installiert und verfügbar sein." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Wenn ausgewählt, testet Back In Time zunächst das entfernte System." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Die Tage werden gezählt, beginnend mit den aktuellen Tag." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Behalte alle Sicherungen, der letzten" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "Tag(e)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Behalte die letzte Sicherung jeden Tages, für die letzten" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "" +"Die Wochen werden ab der aktuell laufenden Woche gezählt. Eine Woche beginnt" +" am Montag." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Behalte die letzte Sicherung jeder Woche, für die letzten" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "Woche(n)." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "Einstellungen wiederherstellen" - -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr " und Ihren Benutzer zur Gruppe »fuse« hinzufügen" - -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" -"Bitte ein Schnappschuss auswählen, von dem Sie die Einstellungen von " -"%(appName)s wiederherstellen möchten.\n" -"Der Pfad kann aussehen wie folgender: \n" -"%(samplePath)s\n" -"\n" -"Wenn sich der Schnappschuss auf einer externen Festplatte befindet, oder " -"verschlüsselt ist, muss er vorher manuell eingehängt werden. Wenn Sie den " -"SSH-Modus verwenden, müssen Sie außerdem die öffentliche Schlüsselanmeldung " -"zum entfernten Rechner einrichten%(addFuse)s.\n" -"Weitere Informationen unter »man backintime«." - -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "Keine Einstellungen gefunden." - -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "Benutzerrückrufskript hat keine Shebang-Zeile (#!/bin/sh)." - -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "Shebang im Benutzerrückrufskript ist nicht ausführbar." - -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Vergleichsoptionen" - -#: ../../qt/snapshotsdialog.py:60 +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "" +"Die Monate werden als Kalendermonate gezählt, beginnend mit dem aktuellen " +"Monat." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Behalte die letzte Sicherung jedes Monats, für die letzten" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "Monat(e)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "" +"Die Jahre werden als Kalenderjahre gezählt, beginnend mit dem aktuellen " +"Jahr." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Behalte die letzte Sicherung jedes Jahres, für" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "alle Jahre." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… freier Speicher weniger ist als" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… freie Inodes weniger sind als" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Älteste Sicherung entfernen, wenn …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "" +"Zur Ausführung von Back In Time als root ist eine Authentifizierung " +"erforderlich." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" +"Zur Änderung von Sicherungszeitplänen, die durch den Anschluss externer " +"Speichergeräte ausgelöst werden, ist eine Authentifizierung erforderlich." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Verknüpfungen" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Orte" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Dateisystem" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Sicherungsverzeichnisse" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: {profile_name} (von Nutzer \"{desktop_user}\")" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Letztes Protokoll ansehen" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "{appname} starten" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "In Arbeit …" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "Manpage: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Konfiguration importieren" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Kein Verzeichnis ausgewählt" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importieren" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Suche …" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" +"Sicherungsverzeichnis wählen, aus dem die Konfiguration importiert werden " +"soll. Der Pfad könnte beispielsweise wie folgt aussehen: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" +"Befindet sich das Verzeichnis auf einem externen oder entfernten Laufwerk, " +"muss dieses vorher manuell eingehängt werden." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Erneut scannen" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Versteckte Verzeichnisse anzeigen" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Versteckte Verzeichnisse zeigen/ausblenden (Strg+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "In diesem Verzeichnis wurde keine Konfiguration gefunden" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Suche abgeschlossen." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Vollständiges Protokoll zeigen" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Countdown zum Herunterfahren" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Die Sicherung ist abgeschlossen." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Herunterfahren abbrechen" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Jetzt herunterfahren" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Das System wird in {n} Sekunde heruntergefahren." +msgstr[1] "Das System wird in {n} Sekunden heruntergefahren." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Optionen zum Vergleichen von Sicherungen" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Befehl:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parameter:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "%1 und %2 als Pfadparameter verwenden" +msgstr "Als Pfadparameter %1 und %2 verwenden" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Nur verschiedene Schnappschüsse auflisten." +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Bitte Diff-Befehl festlegen oder Abbrechen drücken." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "Nur gleiche Schnappschüsse auflisten zu: " +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Der Befehl »{cmd}« kann auf dem System nicht gefunden werden. Bitte etwas " +"anderes versuchen oder Abbrechen drücken." + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" +"Keine Parameter für den diff-Befehl festgelegt. Verwendet wird der " +"Defaultwert »{params}«." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Sicherungen" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Nur sich unterscheidende Sicherungen" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Nur Sicherungen auflisten, die gleich sind mit:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Gründliche Prüfung (genauer, aber langsamer)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "Löschen" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" msgstr "Alles auswählen" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Unterschiede" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Vergleichen" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Gehen zu" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Sie können einen Schnappschuss nicht mit sich selbst vergleichen." +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Optionen" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Es ist nicht möglich, eine Sicherung mit sich selbst zu vergleichen, da der " +"Vergleich redundant wäre." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" +"Soll »{file_or_dir}« in der Sicherung »{backup_id}« wirklich gelöscht " +"werden?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Soll »{file_or_dir}« wirklich in {count} Sicherungen gelöscht werden?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "WARNUNG: Dies kann nicht rückgängig gemacht werden." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "»{path}« von zukünftigen Sicherungen ausschließen?" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Befehl nicht gefunden: %s" +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Root-Modus" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" +msgstr "" +"Back In Time wird derzeit mit Root-Rechten (vollständiger Systemzugriff) " +"ausgeführt" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Heute" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Gestern" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Diese Woche" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Letzte Woche" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -"Soll »%(file)s« wirklich in dem Schnappschuss »%(snapshot_id)s« gelöscht " -"werden?\n" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -"Soll »%(file)s« wirklich in %(count)d Schnappschüssen gelöscht werden?\n" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "ACHTUNG: Das kann nicht rückgängig gemacht werden!" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Letzte Überprüfung {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "»%s« von zukünftigen Schnappschüssen ausschließen?" +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Dies ist KEINE Sicherung, aber eine Live-Ansicht der lokalen Daten." diff --git a/common/po/el.po b/common/po/el.po index 8546de510..6cd544cbe 100644 --- a/common/po/el.po +++ b/common/po/el.po @@ -1,1655 +1,2671 @@ -# Greek translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Dimitris Tzemos +# SPDX-FileCopyrightText: © Iliana Panagopoulou (hpanago) +# SPDX-FileCopyrightText: © Paraskevas Leivadaros # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:25+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Greek \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-02 11:33+0000\n" +"Last-Translator: gnutechie \n" +"Language-Team: Greek \n" +"Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"Όλες οι άδειες χρήσης που χρησιμοποιούνται σε αυτό το έργο βρίσκονται στον " +"κατάλογο {dir_link}. Για να εξαγάγετε πληροφορίες άδειας χρήσης και " +"πνευματικών δικαιωμάτων ανά αρχείο χρησιμοποιώντας μεταδεδομένα SPDX, " +"ανατρέξτε στο {readme_link}." -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Προειδοποίηση" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Το προφίλ \"%s\" υπάρχει ήδη !" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Κύριο Προφίλ" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Αδύνατη η αφαίρεση του τελευταίου προφίλ !" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Τοπικό (EncFS κρυπτογραφημένο)" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Απενεργοποιημένο" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS κρυπτογραφημένο)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Σε κάθε εκκίνηση/επανεκκίνηση" +#: common/config.py:237 +msgid "Local" +msgstr "Τοπικό" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Κάθε 5 λεπτά" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Τοπικά κρυπτογραφημένο" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Κάθε 10 λεπτά" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Κρυπτογράφηση" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Κάθε 30 λεπτά" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Κάθε ώρα" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Ιδιωτικό κλειδί SSH" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Κάθε 2 ώρες" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Προφίλ: \"{name}\"" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Κάθε 4 ώρες" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Ο κατάλογος αντιγράφων ασφαλείας δεν είναι έγκυρος." -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Κάθε 6 ώρες" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Πρέπει να επιλεχτεί τουλάχιστον ένας φάκελος για αντίγραφα ασφαλείας." -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Κάθε 12 ώρες" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Κατάλογος: {path}" -#: ../../common/config.py:87 -msgid "Custom Hours" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Αυτός ο κατάλογος δεν μπορεί να συμπεριληφθεί στο αντίγραφο ασφαλείας, καθώς" +" αποτελεί μέρος του ίδιου του προορισμού δημιουργίας αντιγράφων ασφαλείας." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Καθημερινά" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" +"Η τιμή για την επιλογή \"Κατάργηση του παλαιότερου αντιγράφου ασφαλείας εάν " +"ο ελεύθερος χώρος είναι μικρότερος από\" ({val_one}) πρέπει να είναι " +"μικρότερη ή ίση με το όριο για την επιλογή \"Προειδοποίηση εάν ο ελεύθερος " +"χώρος στο δίσκο πέσει κάτω από\" ({val_two})." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" +"Παρακαλώ προσαρμόστε τις ρυθμίσεις έτσι ώστε το όριο κατάργησης αντιγράφων " +"ασφαλείας να μην είναι υψηλότερο από το όριο προειδοποίησης." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Όταν συνδέεται δίσκος" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Ο χρονοπρογραμματισμός με Udev δεν δουλεύει με τη λειτουργία {mode}" -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Κάθε εβδομάδα" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Αποτυχία εγγραφής νέου crontab." -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Κάθε μήνα" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Το Cron δεν εκτελείται, παρόλο που η εντολή crontab είναι διαθέσιμη. Οι " +"προγραμματισμένες εργασίες δημιουργίας αντιγράφων ασφαλείας δεν θα " +"εκτελεστούν. Το Cron ενδέχεται να είναι εγκατεστημένο αλλά όχι " +"ενεργοποιημένο. Δοκιμάστε να εκτελέσετε τις δύο εντολές \"systemctl enable " +"cron\" και \"systemctl start cron\" ή συμβουλευτείτε τα κανάλια υποστήριξης " +"της διανομής GNU/Linux που χρησιμοποιείται αυτήν τη στιγμή για βοήθεια." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Αποτυχία αποθήκευσης της παραμετροποίησης" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Αποτυχία φόρτωσης της παραμετροποίησης" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Το προφίλ \"{name}\" υπάρχει ήδη." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Το τελευταίο προφίλ δεν μπορεί να αφαιρεθεί." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Αδυναμία προσάρτησης '{command}'" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Η παραμετροποίηση για κρυπτογραφημένο φάκελο δεν βρέθηκε." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Δημιουργία νέου κρυπτογραφημένου φακέλου;" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Ακύρωση" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Ημέρα(ες)" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Παρακαλώ εισάγετε ξανά τον κωδικό πρόσβασης EncFS για επιβεβαίωση." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Εβδομάδα(ες)" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "Οι κωδικοί πρόσβασης του EncFS δεν ταιριάζουν." -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Έτος(η)" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Λήψη στιγμιότυπου" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Αδυναμία αρχικοποίησης κρυπτογραφημένου μονοπατιού '{command}'" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Αδυναμία αποπροσάρτησης {mountprocess} από το {mountpoint}." -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" +"Δεν βρέθηκε το {command}. Παρακαλώ εγκαταστήστε το (π.χ. μέσω " +"\"{installcommand}\")" -#: ../../common/config.py:129 -msgid "Local" -msgstr "Τοπικό" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Το σημείο προσάρτησης {mntpoint} δεν είναι κενό." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Καταχώρηση κωδικού για {mode} προφίλ \"{profile}\":" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" +"Δεν ήταν δυνατή η εγκατάσταση του κανόνα Udev για το προφίλ {profile_id}. Η " +"υπηρεσία DBus '{dbus_interface}' δεν ήταν διαθέσιμη." -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Τοπικό κρυπτογραφημένο" - -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Κρυπτογράφηση" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Δεν βρέθηκε το UUID για το {path}" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH κρυπτογραφημένο" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "ΑΠΟΤΥΧΙΑ" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Προεπιλογή" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Επαναφορά δικαιωμάτων" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Ολοκληρώθηκε" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Οι ακόλουθες καταχωρήσεις από τη λίστα συμπερίληψης δεν έχουν αντίστοιχο " +"αρχείο ή κατάλογο στην πηγή αντιγράφων ασφαλείας:" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Αναβολή του backup όσο το σύστημα βρίσκεται σε μπαταρία" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Δεν είναι δυνατή η εύρεση του καταλόγου αντιγράφων ασφαλείας.." -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Εάν βρίσκεται σε αφαιρούμενη μονάδα δίσκου, συνδέστε την." -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Αναμονή {n} δευτερόλεπτο." +msgstr[1] "Αναμονή {n} δευτερόλεπτα." -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Αποτυχία δημηιουργίας αντιγράφου ασφαλείας {snapshot_id}." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Παρακαλώ να είστε υπομονετικοί. Οριστικοποίηση…" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Δεν είναι δυνατή η δημιουργία καταλόγου." -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Αποθήκευση αρχείου ρυθμίσεων…" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Αποθήκευση δικαιωμάτων…" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "" +"Βρέθηκε ένα μη ολοκληρωμένο αντίγραφο ασφαλείας {snapshot_id} του οποίου η " +"δημιουργία μπορεί να συνεχιστεί." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Κύριο Προφίλ" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "" +"Αφαίρεση του ελλιπούς καταλόγου {snapshot_id} από την τελευταία εκτέλεση" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Προφίλ: \"%s\"" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Αδυναμία αφαίρεσης καταλόγου" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Ο φάκελος στιγμιοτύπων δεν είναι έγκυρος !" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Δημιουργία αντιγράφων ασφαλείας" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Πρέπει να διαλέξετε τουλάχιστον ένα φάκελο για backup!" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Επιτυχία" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Δεν μπορείτε να συμπεριλάβετε φάκελο backup !" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Μερική μεταφορά λόγω σφάλματος" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Δεν μπορείτε να συμπεριλάβετε υποφάκελο backup !" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "Μερική μεταφορά λόγω εξαφανισμένων αρχείων πηγής (δείτε 'man rsync')" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "Το %s δεν είναι φάκελος !" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "Η διεργασία 'rsync' τελείωσε με κωδικό {exit_code}" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Δείτε το 'man rsync' για περισσότερες λεπτομέρειες" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -"Αδύνατη η εγγραφή στο: %s\n" -"Είστε σίγουροι ότι έχετε δικαίωμα εγγραφής;" +"Οι αρνητικοί αριθμοί ως κωδικοί εξόδου του rsync είναι αριθμοί σήματος, " +"δείτε 'kill -l' και 'man kill'" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Δεν άλλαξε τίποτα, δεν χρειάζεται νέο αντίγραφο ασφαλείας" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Αδυναμία μετονομασίας {new_path} σε {path}." -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Εφαρμογή κανόνων για την κατάργηση παλιών αντιγράφων ασφαλείας" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Εφαρμόστε πολιτική διατήρησης" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Προσπάθεια διατήρησης ελάχιστου ελεύθερου χώρου" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" msgstr "" -"Δε βρέθηκε crontab.\n" -"Είστε σίγουροι ότι το cron είναι εγκατεστημένο ;\n" -"Αν όχι, θα πρέπει να απενεργοποιήσετε όλα τα αυτοματοποιημένα back up." +"Προσπαάθεια διατήρησης ελάχιστου ποσοστού {perc} ελεύθερων συστημάτων " +"αρχείου (inodes)" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Τώρα" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Αδυναμία προσάρτησης {sshfs}" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." msgstr "" +"το ssh-agent δε βρέθηκε. Παρακαλώ βεβαιωθείτε ότι είναι εγκατεστημένο." -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Δεν βρέθηκε UUID για \"%s\"" - -#: ../../common/encfstools.py:86 -#, python-format +#: common/sshtools.py:489 msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" - -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"Δεν ήταν δυνατό το ξεκλείδωμα του ιδιωτικού κλειδιού SSH. Λάθος κωδικός ή " +"δεν υπάρχει διαθέσιμος κωδικός για cron." -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Δημιουργία νέου κρυπτογραφημένου φακέλου;" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Η απομακρυσμένη διαδρομή υπάρχει αλλά δεν είναι φάκελος." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Ακύρωση" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Η απομακρυσμένη διαδρομή δεν είναι εγγράψιμη." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Παρακαλώ επιβεβαιώστε τον κωδικό" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Η απομακρυσμένη διαδρομή δεν είναι εκτελέσιμη." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Ο κωδικός δεν ταιριάζει" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Αδυναμία δημιουργίας απομακρυσμένης διαδρομής." -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" msgstr "" +"Ο απομακρυσμένος διακομιστής {host} δεν υποστηρίζει την εντολή {command}" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Λήψη στιγμιοτύπου" - -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" +"Οι εντολές ελέγχου στον διακομιστή {host} επέστρεψαν ένα άγνωστο σφάλμα" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "O απομακρυσμένος διακομιστής {host} δεν υποστηρίζει hardlinks" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" +"Αντιγράψτε το δημόσιο κλειδί SSH \"{pubkey}\" στον απομακρυσμένο διακομιστή " +"\"{host}\"." -#: ../../common/mount.py:590 -#, python-format +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Παρακαλώ επιβεβαιώστε τον κωδικό του \"{user}\"." + +#: common/tools.py:382 +#, python-brace-format msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"Το σύστημα αρχείων του προορισμού για το {path} είναι μορφοποιημένο με NTFS," +" το οποίο είναι ασύμβατο με συστήματα αρχείων προορισμού Unix." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} δεν είναι έγκυρος κατάλογος." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Η δημιουργία του ακόλουθου καταλόγου απέτυχε:" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Τα δικαιώματα εγγραφής μπορεί να είναι περιορισμένα." -#: ../../common/snapshotlog.py:62 +#: common/tools.py:471 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"Το σύστημα αρχείων του προορισμού για το {path} είναι μορφοποιημένο με FAT " +"το οποίο δεν υποστηρίζει hard-links. Παρακαλώ χρησιμοποιήστε ένα εγγενές " +"σύστημα αρχείων GNU/Linux." -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "ΑΠΟΤΥΧΙΑ" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"Το σύστημα αρχείων προορισμού για το {path} είναι ένα κοινόχρηστο στοιχείο " +"που προσαρτάται μέσω SMB. Βεβαιωθείτε ότι ο απομακρυσμένος διακομιστής SMB " +"υποστηρίζει συμβολικούς συνδέσμους ή ενεργοποιήστε το \"{copyLinks}\" στο " +"\"{expertOptions}\"." -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Αντιγραφή συνδέσμων (αφαίρεση αναφοράς των συμβολικών συνδέσμων)" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Προχωρημένες Ρυθμίσεις" -#: ../../common/snapshots.py:669 +#: common/tools.py:491 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" -"Δε βρέθηκε ο φάκελος στιγμιοτύπων.\n" -"Εάν βρίσκεται σε αφαιρούμενη συσκευή, παρακαλώ συνδέστε την." +"Το σύστημα αρχείων προορισμού για το {path} είναι ένα κοινόχρηστο στοιχείο " +"που προσαρτάται μέσω sshfs. Το Sshfs δεν υποστηρίζει σκληρούς συνδέσμους. " +"Χρησιμοποιήστε τη λειτουργία \"SSH\"." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Αναμονή %s δευτερόλεπτο." -msgstr[1] "Αναμονή %s δευτερόλεπτα." +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Αδυναμία δημιουργίας φακέλου στον κατάλογο:" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Αποτυχία λήψης στιγμιοτύπου %s !!!" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "ΣΧετικά με Back In Time" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Οριστικοποίηση" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Πνευματική ιδιοκτησία:" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Αδύνατη η δημιουργία του φακέλου: %s" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Συγγραφείς:" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Μεταφραστές:" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" +"Dimitris Tzemos \n" +"Iliana Panagopoulou (hpanago)\n" +"Paraskevas Leivadaros " -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." msgstr "" +"Οι έπαινοι του μεταφραστή δεν είναι διαθέσιμοι για την τρέχουσα γλώσσα." -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "αυτός ο σύνδεσμος" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" +"Ακολουθήστε {thislink} για να λάβετε έπαινους μεταφραστή για όλες τις " +"γλώσσες." -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Ιστοσελίδα του έργου" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Εγχειρίδιο χρήστη" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Ανοίξτε το εγχειρίδιο χρήστη στο πρόγραμμα περιήγησης (τοπικά εάν είναι " +"διαθέσιμο διαφορετικά στο διαδίκτυο)" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Λήψη στιγμιοτύπου" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Έκδοση{BOLDEND}: {version}" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: qt/app.py:332 +#, fuzzy, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"{app_name} φαίνεται να εκτελείται για πρώτη φορά χωρίς να εντοπίζεται " +"παραμετροποίηση." -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" +#: qt/app.py:337 +#, fuzzy +msgid "" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Εισαγωγή υπάρχουσας παραμετροποίησης (από κατάλογο ανάκτησης ή από άλλο " +"υπολογιστή);" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Στη συνέχεια, πατήστε OK." -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Δημιουργήστε ένα αντίγραφο ασφαλείας" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Χρήση ώρας τροποποίησης και μεγέθους, για ανίχνευση αλλαγής αρχείου." -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Δημιουργία αντιγράφου ασφαλείας (λειτουργία ελέγχου αθροίσματος)" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "ΜΕ ΛΑΘΗ !" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Χρήση των checksum για ανίχνευση αλλαγών αρχείου." -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Τώρα" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Παύση της διαδικασίας δημιουργίας αντιγράφων ασφαλείας" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Συνέχιση της διαδικασίας δημιουργίας αντιγράφων ασφαλείας" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Διακοπή της διαδικασίας δημιουργίας αντιγράφων ασφαλείας" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Ανανέωση λίστας αντιγράφων ασφαλείας" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Όνομα αντίγραφου ασφαλείας" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Κατάργηση αντιγράφου ασφαλείας" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Άνοιγμα αρχείου καταγραφής αντιγράφων ασφαλείας" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Προβολή αρχείου καταγραφής του επιλεγμένου αντιγράφου ασφαλείας." -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Άνοιγμα αρχείου καταγραφής τελευταίου αντιγράφου ασφαλείας" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Δείτε το αρχείο καταγραφής του τελευταίου αντιγράφου ασφαλείας." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Διαχείριση προφιλ…" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Επεξεργασία επανάκλησης χρήστη" -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:532 +msgid "Shutdown" +msgstr "Τερματισμός" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." msgstr "" +"Τερματίστε τη λειτουργία του συστήματος μετά την ολοκλήρωση της δημιουργίας " +"αντιγράφων ασφαλείας." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Καθορισμός γλώσσας…" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:540 +msgid "Exit" +msgstr "Έξοδος" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man page: Back In Time" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Εμφανίζει τη σελίδα χρήστη σχετικά με το Back In Time (backintime)" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man page: Αρχείο διαμόρφωσης προφίλ" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" +"Εμφανίζει τη σελίδα χρήστη σχετικά με το αρχείο διαμόρφωσης προφίλ " +"(backintime-config)" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Ανοίξτε τον ιστότοπο Back In Time στο πρόγραμμα περιήγησης" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "" +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "Αρχείο αλλαγών" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" +"Ανοίξτε το εγχειρίδιο χρήστη στο πρόγραμμα περιήγησης (τοπικά εάν είναι " +"διαθέσιμο, διαφορετικά στο διαδίκτυο)" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "" +#: qt/app.py:573 +msgid "FAQ" +msgstr "Συχνές Ερωτήσεις" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Ανοίξτε τις Συχνές Ερωτήσεις (FAQ) στο πρόγραμμα περιήγησης" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "Κάνε μια ερώτηση" -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "Αναφορά προβλήματος" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:584 +msgid "Translation" +msgstr "Μετάφραση" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Εμφανίζει ξανά το μήνυμα σχετικά με τη συμμετοχή στη μετάφραση." -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Μετάβαση κρυπτογράφησης (EncFS)" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Εμφανίζει ξανά το μήνυμα σχετικά με την αφαίρεση του EncFS." -#: ../../qt/app.py:163 -msgid "Website" -msgstr "" +#: qt/app.py:599 +msgid "About" +msgstr "Σχετικά" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" -msgstr "" +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Επαναφορά" -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "Επανέφερε τα επιλεγμένα αρχεία ή φακέλους στον αρχικό τους προορισμό." -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "" +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Επαναφορά στο…" -#: ../../qt/app.py:171 -msgid "Report a bug" +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "Επανέφερε τα επιλεγμένα αρχεία ή φακέλους σε ένα νέο προορισμό." + +#: qt/app.py:615 +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Επαναφορά του τρέχοντος εμφανιζόμενου φακέλου και όλων των περιεχομένων του," +" στον αρχικό προορισμό." -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" +#: qt/app.py:621 +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" +"Επανέφερε τον τρέχοντα εμφανιζόμενο φάκελο και όλα τα περιεχόμενα του σε ένα" +" νέο προορισμό." -#: ../../qt/app.py:204 +#: qt/app.py:624 msgid "Up" -msgstr "" +msgstr "Επάνω" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" -msgstr "" +msgstr "Προβολή κρυφών αρχείων" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Σύγκριση αντιγράφων ασφαλείας…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Απελευθέρωση υποψηφίου" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Εμφανίζει ξανά το μήνυμα για αυτόν τον υποψήφιο έκδοσης." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Backup" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Eπαναφορά" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Βοήθεια" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../qt/app.py:260 -msgid "Snapshot" +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../qt/app.py:271 -msgid "View" +#: qt/app.py:784 +msgid "Light icon" msgstr "" -#: ../../qt/app.py:321 -msgid "Shortcuts" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Μόνο εικονίδια" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:811 +msgid "Text only" +msgstr "Κείμενο μόνο" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Κείμενο κάτω από τα εικονίδια" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Κείμενο δίπλα στο εικονίδιο" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Αυτός ο κατάλογος δεν υπάρχει\n" +"στο τρέχον επιλεγμένο αντίγραφο ασφαλείας." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"Αυτός ο κατάλογος δεν υπάρχει\n" +"στο τρέχον επιλεγμένο αντίγραφο ασφαλείας." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Εάν αυτό το παράθυρο είναι κλειστό, το Back In Time δεν θα μπορεί να " +"τερματίσει τη λειτουργία του συστήματός σας όταν ολοκληρωθεί η δημιουργία " +"αντιγράφων ασφαλείας." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Να κλείσω το παράθυρο ούτως ή άλλως;" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" -msgstr "" +msgstr "Ολοκληρώθηκε, δεν χρειάζεται backup" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "" +#: qt/app.py:1260 +msgid "Working:" +msgstr "Εργασία:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Εργασία" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Σφάλμα" + +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "Απεσταλμένα:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "" +msgstr "Ταχύτητα:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "" +msgstr "ΕΧΑ:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Αντίγραφα ασφαλείας:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Επαναφορά {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Επαναφορά {path} στο…" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Γειά\n" +"Μέχρι στιγμής έχεις χρησιμοποιήσει μερικές φορές την {language} γλώσσα\n" +"Η μετάφραση της εγκατεστημένης έκδοσης του Back In Time στα {language} είναι {perc} ολοκληρωμένη. Ανεξάρτητα απο το τεχνικό σου επίπεδο, μπορείς να συνεισφέρεις στην μετάφραση και κατ' επέκταση στο ίδιο το Back In Time.\n" +"Παρακαλώ επισκέψου το {translation_platform_url} εαν επιθμείς να συνεισφέρεις. Για περαιτέρω βοήθεια και ερωτήσεις , παρακαλώ επισκέψου το {back_in_time_project_website}.\n" +"Μας συγχωρείς για την διακοπή, και αυτό το μήνυμα δεν θα εμφανιστεί ξανά. Αυτή η ενημέρωση είναι διαθέσιμη οπότε θελήσεις μέσα απο το μενού help.\n" +"H ομάδα του Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "πλατφόρμα μετάφρασης" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Ιστοσελίδα" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Η μετάφρασή σου" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Στο Fediverse στο Mastodon: {link_and_label}." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Λίστα αλληλογραφίας {link_and_label}." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Λίστα αλληλογραφίας {link_and_label}." -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} στην ιστοσελίδα του έργου." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Άνοιξε ένα ζήτημα" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." msgstr "" +"Εναλλακτικά, μπορείς να χρησιμοποιήσεις εναλλακτικό κανάλι επικοινωνίας της " +"επιλογής σου." -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Αυτή είναι μια έκδοση συνεισφοράς του Back In Time και ο πρωταρχικός της σκοπός είναι να δοκιμαστεί η σταθερότητά της κατά το στάδιο προετοιμασίας της επόμενης επίσημης έκδοσης.\n" +"Δεν συλλέγονται προσωπικά δεδομένα ή δεδομένα τηλεμετρίας. Ωστόσο η ομάδα του Back In Time ενδιαφέρεται να γνωρίζει εάν αυτή η έκδοση συνεισφοράς χρησιμοποιείται και αν αξίζει να συνεχίσει να προσφέρει αυτές τις εκδόσεις πριν την επίσημη κυκλοφορία.\n" +"Συνεπώς, η ομάδα ευγενικά επιθυμεί να σας ζητήσει την ανατροφοδότησή σας σε οτιδήποτε έχετε δοκιμάσει από την συγκεκριμένη έκδοση, ακόμα και αν δεν παρουσιάστηκαν ιδιαίτερα ζητήματα. Ακόμα και ένα γρήγορο τέστ μερικών λεπτών θα ήταν πολύτιμο για εμάς. \n" +"Οι ακόλουθοι τρόποι επικοινωνίας είναι διαθέσιμοι:\n" +"{contact_list}\n" +"Στην συγκεκριμένη έκδοση, αυτό το μήνυμα δεν θα εμφανιστεί εκ νέου όμως θα είναι προσβάσιμο μέσω του μενού βοήθειας.\n" +"Σας ευχαριστούμε για την υποστήριξη και για την βοήθεια να εξελίξουμε το Back In Time!\n" +"Η δική σου ομάδα του Back In Time" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Μόνο {free} ελεύθερος χώρος διαθέσιμος στον προορισμό, ο οποίος είναι κάτω " +"από το διαμορφωμένο όριο {threshold}." -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Συνέχεια με τη δημιουργία αντιγράφων ασφαλείας;" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" +"Όλα τα νεότερα αρχεία στη {path} θα καταργηθούν. Θέλετε να συνεχίσετε;" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Όλα τα νεότερα αρχεία στον αρχικό κατάλογο θα καταργηθούν. Θέλετε να " +"συνεχίσετε;" -#: ../../qt/app.py:1105 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Προειδοποίηση{BOLDEND}: Η διαγραφή αρχείων στη ρίζα του συστήματος " +"αρχείων θα μπορούσε να προκαλέσει βλάβη σε ολόκληρο το σύστημα." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Όνομα αντιγράφου ασφαλείας" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Κατάργηση αυτού του αντιγράφου ασφαλείας;" +msgstr[1] "Να καταργηθούν αυτά τα αντίγραφα ασφαλείας;" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" +"Η επιλογή γλώσσας ενεργοποιείται έπειτα από επανεκκίνηση του Back In Time." -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Aντίγραφα ασφαλείας βάσει εκδόσεων (υπαρχρήστης)" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"Φιλικό προς το χρήστη γραφικό περιβάλλον χρήστη (GUI) για αντίγραφα " +"ασφαλείας με βάση τις εκδόσεις που μειώνει τη χρήση του δίσκου (κατάσταση " +"υπερχρήστη)" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Aντίγραφα ασφαλείας βάσει εκδόσεων" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Φιλικό προς το χρήστη γραφικό περιβάλλον χρήστη (GUI) για αντίγραφα " +"ασφαλείας με βάση τις εκδόσεις που μειώνει τη χρήση του δίσκου" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Ερώτηση" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Δημιουργία αντιγράφων backup με επίθεμα {suffix} πριν την αντικατάσταση ή " +"αφαίρεση τοπικών στοιχείων." -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Πριν από την επαναφορά, οι νεότερες εκδόσεις των αρχείων θα μετονομαστούν με" +" το προσαρτημένο {suffix}. Αυτά τα αρχεία μπορούν να καταργηθούν με την " +"ακόλουθη εντολή:" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Αποκατάσταση μόνο στοιχείων που δεν υπάρχουν ή είναι νεότερα από αυτά στον " +"προορισμό. Χρησιμοποιώντας την επιλογή {rsync_example}." -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Αφαίρεσε νέα στοιχεία από τον αρχικό φάκελο." -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Επαναφέρετε τα επιλεγμένα αρχεία ή καταλόγους στον αρχικό προορισμό και " +"διαγράψτε αρχεία ή καταλόγους που δεν βρίσκονται στο αντίγραφο ασφαλείας. Να" +" είστε εξαιρετικά προσεκτικοί, καθώς αυτό θα διαγράψει αρχεία και καταλόγους" +" που εξαιρέθηκαν κατά τη δημιουργία του αντιγράφου ασφαλείας." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"Θέλετε πραγματικά να επαναφέρετε αυτό το στοιχείο στον νέο κατάλογο;" +msgstr[1] "" +"Θέλετε πραγματικά να επαναφέρετε αυτά τα στοιχεία στον νέο κατάλογο;" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Θέλετε πραγματικά να επαναφέρετε αυτό το στοιχείο;" +msgstr[1] "Πραγματικά να αποκαταστήσετε αυτά τα στοιχεία;" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "User-callback: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"Το σενάριο επανάκλησης χρήστη πρέπει να περιλαμβάνει ένα shebang στην πρώτη " +"γραμμή (π.χ. {example})." -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Εμφάνιση/απόκρυψη κρυφών αρχείων και καταλόγων (Ctrl+H)" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Προσθήκη στα Περιλαμβανόμενα" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Προσθήκη στα Αποκλειόμενα" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Γλώσσα εγκατάστασης" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Μεταφρασμένο: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Προεπιλογή συστήματος" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Χρησιμοποιήστε τη γλώσσα του λειτουργικού συστήματος." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Προβολή αρχείου καταγραφής αντιγράφων ασφαλείας" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Προβολή Τελευταίου αρχείου καταγραφής" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "Προφίλ:" + +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Αντίγραφα ασφαλείας:" + +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "Φίλτρο:" -#: ../../qt/logviewdialog.py:110 +#: qt/logviewdialog.py:100 msgid "[E] Error, [I] Information, [C] Change" -msgstr "" +msgstr "[E] Σφάλμα, [I] Πληροφορία, [C] Αλλαγή" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 msgid "decode paths" -msgstr "" +msgstr "αποκωδικοποίηση μονοπατιών" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "Όλα" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Αλλαγές" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Σφάλματα" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Πληροφορία" +msgstr[1] "Πληροφορίες" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "αποτυχίες μεταφοράς rsync (πειραματικό)" + +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Διαχείριση προφίλ" + +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Επεξεργασία" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Προσθήκη" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Αφαίρεση" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Γενικά" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Συμπεριλαμβάνει" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "Ε&ξαίρεση" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Κατάργηση & Διατήρηση" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Επιλογές" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "Επιλογές E&expert" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Επαναφορά παραμετροποίησης" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Νέο προφίλ" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Μετονομασία προφίλ" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Διαγραφή του προφίλ \"{name}\";" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Αντέγραψε συμβολικούς συνδέσμους ως αρχεία" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Αντέγραψε συμβολικούς συνδέσμους ως πραγματικά αρχεία ή καταλόγους στο " +"αντίγραφο ασφαλείας. Επέλεξε αν θα αντιγραφούν όλοι οι σύνδεσμοι ή μόνο " +"αυτοί που δείχνουν έξω από την πηγή." -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Η επιλογή αυτή ίσως αυξήσει το μέγεθος του αντίγραφου ασφαλείας." + +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Απενεργοποιημένο εκ προεπιλογής." + +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Όλοι οι συμβολικοί σύνδεσμοι αντικαθίστανται από πραγματικά αρχεία ή " +"καταλόγους στους οποίους δείχνουν. Αυτό θα αυξήσει το μέγεθος του αντίγραφου" +" ασφαλείας και ίσως να αποθηκεύσει τα ίδια αρχεία πολλαπλές φορές." -#: ../../qt/qttools.py:220 -msgid "Today" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Χρησιμοποιεί 'rsync --copy-links'." + +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Μόνο εξωτερικά" + +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/qttools.py:224 -msgid "Yesterday" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/qttools.py:229 -msgid "This week" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" msgstr "" -#: ../../qt/qttools.py:233 -msgid "Last week" +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "Παύση της διαδικασίας δημιουργίας αντιγράφων ασφαλείας" + +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "Εξαίρεση αρχείων" + +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" msgstr "" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" msgstr "" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Φάκελοι ανάκτησης" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" msgstr "" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "Αδυναμία αφαίρεσης καταλόγου" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Αποθετήριο εφαρμογής και εκτελέσιμου αρχείου Flatpak" + +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Εμφάνιση κρυφών αρχείων" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" msgstr "" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Διακοπή της διαδικασίας δημιουργίας αντιγράφων ασφαλείας" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Εξαίρεση καταλόγων" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "προεπιλογή" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" -msgstr "" +msgstr "Προγραμματισμός" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "" +msgstr "Ημέρα:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "" +msgstr "Καθημερινές:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Ώρα:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" +msgstr "Ώρα(ες):" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "μετά την ώρα" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Λεπτά:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" +"Εκτελέστε την επιλογή Back In Time μόλις συνδεθεί η μονάδα δίσκου (μόνο μία " +"φορά κάθε X ημέρες). Θα εμφανιστεί ένα μήνυμα κωδικού πρόσβασης sudo." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Τρέξτε το Back In Time επανειλημμένα. Αυτό είναι χρήσιμο εάν ο υπολογιστής " +"δεν λειτουργεί τακτικά." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" +msgstr "Κάθε:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Ενεργοποίηση καταγραφής μηνυμάτων εντοπισμού σφαλμάτων" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Γράφει μηνύματα σε επίπεδο εντοπισμού σφαλμάτων στο αρχείο καταγραφής " +"συστήματος μέσω \"--debug\"." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Προσοχή: Χρησιμοποιήστε το μόνο προσωρινά για διαγνωστικά, καθώς παράγει " +"μεγάλη ποσότητα εξόδου." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Απενεργοποιημένο" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Σε κάθε εκκίνηση/επανεκκίνηση" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Κάθε {n} λεπτό" +msgstr[1] "Κάθε {n} λεπτά" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Κάθε ώρα" +msgstr[1] "Κάθε {n} ώρες" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Ώρες που μπορείτε να τις προσαρμόσετε" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Καθημερινά" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Επανειλημμένως" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Όταν συνδέεται δίσκος (udev)" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Κάθε εβδομάδα" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Κάθε μήνα" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Κάθε χρόνο" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Ώρα(ες)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Ημέρα(ες)" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Εβδομάδα(ες)" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Μήνας(ες)" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Οι προσαρμοσμένες ώρες μπορούν να είναι μόνο μια λίστα ωρών διαχωρισμένων με" +" κόμματα (π.χ. 8,12,18,23) ή */3 για περιοδικά αντίγραφα ασφαλείας κάθε 3 " +"ώρες." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Ŝanĝoj" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Elekti ekzistantan dosieron je privata ŝlosilo de ie alia." + +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Krei novan SSH-ŝlosilon sen pasfrazo." + +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Plena vojo: {path}" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "informoj" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Privata ŝlosilo:" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Uzi SSH-agordon de la sistemo" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Lasas la dosieron je privata ŝlosilo neselektita. Konektoj de SSH fidas al " +"la ekzistanta klient-agordo de la sistemo (ekz., ~/.ssh/config)." -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH-prokurilo" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Gastiganto:" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Pordo:" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Uzanto:" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Konekti al la celgastiganto per tio prokurilo (ankaŭ konita kiel \"jump " +"host\" aŭ \"salt-gastiganto\"). Por pli multe da detaloj, vidu \"-J\" en la " +"dokumentado de la komando \"ssh\" au \"ProxyJump\" en la manpaĝo." -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Informo:{ENDBOLD}: Je 'kriptita SSH' reĝimo, sole unuoblaj kaj duoblaj" +" asteriskoj funkcii (ekz. {example2}). Aliaj tipoj de ĵokeroj kaj ŝablonoj " +"estos ignoritaj (ekz. {example1}). Nomoj de dosieroj estas neantaŭvideblaj " +"je tio reĝimo pro la kriptado de EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Ekskluzivi ŝablonojn, dosierojn aŭ dosierujojn" + +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Aldoni ŝablonon" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Aldoni dosierojn" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Aldoni dosierujojn" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Sugestoj" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "Elektu de ordinare uzitaj eroj por aldoni al la savkopi-ekskludoj." + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Ekskluzivi dosierojn pli grandajn ol:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Ekskluzivi dosierojn pli grandajn ol {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Se 'Tuta rsync reĝimo' estas malvalidigita, tio sole influos novajn " +"doiserojn, ĉar por rsync tio estas transmet-opcio, ne ekskluziv-opcio. Do " +"grandaj dosieroj, kiujn estis savkopiita antaŭe, restos en savkopioj, eĉ se " +"ili ŝanĝis." -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Laborante..." +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Ekskludi ŝablonon" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Hodiaŭ" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Enigi eksklud-ŝablonon:" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Hieraŭ" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Por helpo, vidu la sekcion de la manpaĝo de rsync nomita {link}." -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Nuna semajno" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Malfermi la manpaĝon de rsync" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Lasta semajno" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Ekskludi dosierojn" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Ekskludi dosierujojn" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "Malŝaltita, ĉar tio ŝablono ne funkcias je 'kriptita SSH' reĝimo." -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Tio ĉi opcioj estas por specialaj agordoj. Modifi sole se plene konscia pri " +"ilia rezultatoj." -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Redakti" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Startas 'rsync' je '{cmd}':" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" +msgstr "kiel cron laboro" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" +msgstr "je la fora gastiganto" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "kiam fari manan savkopion" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Ĝenerale" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Bonvolu instali 'nocache' por ŝalti tion opcion." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:116 +msgid "on local machine" +msgstr "je loka komputilo" + +#: qt/manageprofiles/tab_expert_options.py:130 +msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Alidirekti stdout al /dev/null en cron laboro." -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:136 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron aŭtomate sendos retpoŝto kun la aldonita eligo de la cron laboroj, se " +"MTA estas instalita." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." +msgstr "Alidirekti stderr al /dev/null en cron laboro." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron aŭtomate sendos retpoŝto kun la aldonita erar-eligo de la cron laboroj," +" se MTA estas instalita." -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/s" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Trafiklimigi rsync-on:" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:204 +msgid "Preserve ACL" +msgstr "Konservi ACL-ojn" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Uzanto:" +#: qt/manageprofiles/tab_expert_options.py:222 +msgid "Preserve extended attributes (xattr)" +msgstr "Konservi etenditajn atributojn (xattr)" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Limigi al unu dosiersistemo" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Opcioj devas esti citita ekz. {example}." -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "Aldoni pluajn opciojn al rsync" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefikso antaŭ ĉiuj komandoj en la fora gastiganto." -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format +msgid "" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Variabloj devas eskapita je \\$FOO. Tio ne afektas rsync. Do por aldoni " +"prefikso al rsync, uzi \"{example_value}\" je {rsync_options_value}." -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "defaŭlto" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Aldoni prefikso al SSH-komandoj" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Kontroli, ke la fora gastiganto estas konektita" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Averto: se malvalidigita kaj la fora gastiganto ne estas disponebla, tio " +"eblas igi kelkaj da bizaraj eraroj." -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Kontroli, ke la fora gastiganto subtenas ĉiujn necesajn komandojn." -#: ../../qt/settingsdialog.py:307 -msgid "Schedule" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Averto: Se malvalidigita kaj la fora gastiganto ne subtenas ĉiujn necesajn " +"komandojn, tio eblas igi kelkaj da bizaraj eraroj." -#: ../../qt/settingsdialog.py:317 -msgid "Day:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(defaŭlto: {})" -#: ../../qt/settingsdialog.py:328 -msgid "Weekday:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "malŝaltita" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Horo:" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "ŝaltita" -#: ../../qt/settingsdialog.py:350 -msgid "Hours:" -msgstr "" +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Maniero:" -#: ../../qt/settingsdialog.py:359 -msgid "" -"Run Back In Time repeatedly. This is useful if the computer is not running " -"regularly." -msgstr "" +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Kien savi la savkopiojn" -#: ../../qt/settingsdialog.py:364 -msgid "Every:" -msgstr "" +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Agordo de SSH" -#: ../../qt/settingsdialog.py:382 -msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Vojo:" -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Inkluzivi" +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Dosiero je privata ŝlosilo:" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Pasvorto" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Savi pasvorton je ŝlosilaro" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Aldoni dosieron" +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" +"Kaŝmemorigi pasvorton por cron (Sekureca problemo: ĉefuzanto eblas legi " +"pasvorton)" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Aldoni dosierujon" +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Progresinta" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Ekskluzivi" +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Plena savkopi-vojo:" -#: ../../qt/settingsdialog.py:434 +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "Planado estas malvalidigita ĉar instalo de cron ne estas trovita." -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "La cela vojo de la savkopio ne povas esti malplena." -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "La ĉifrada pasvorto ne povas esti malplena." -#: ../../qt/settingsdialog.py:476 -msgid "Add default" +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"Eraro okazis dum provi ensaluti en la foriga gastiganto. La sekvanta erar-" +"mesaĝo estis revenigita:" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Por ŝalti ensaluton sen pasvorto, la publika ssh-ŝloliso eblas esti kopiita " +"al la foriga gastiganto." + +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Procedi je kopii la SSH-ŝlosilon?" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"La publika SSH-ŝlosilo ne povis esti kopiita. Tio eble estas pro problemo " +"kun konekto aŭ permeso." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "La aŭtenteco de la gastiganto {host} ne estis konstatebla." -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "" +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} ŝlosilfingropremo estas:" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Bonvolu konfirmi tion fingropremon! Ĉu vi volas aldoni ĝin al via " +"'known_hosts' dosiero?" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Vere ŝanĝi la dosierujon por savkopioj?" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "La elektita celloko por la savkopio ne estas malplena." -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Ĝi devas esti malplena por uzi ĉifradon." -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Nevalida dosiero: Ne estas privata ŝlosilo de ssh" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"La elektita dosiero ({path}) estas publika ssh-ŝloliso. Bonvolu elekti la " +"korespondantan dosieron je privata ŝlosilo anstataŭe (sen \".pub\")." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" +"La dosiero {path} ekzistas jam. Ne povas krei novan SSH-ŝlosilon kun tio " +"nomo." -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Malsukcesi krei novan SSH-ŝlosilon en {path}." -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Inkluzivi dosierojn kaj dosierujojn" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"\"{path}\" estas simbola ligilo. La ligita celo ne estos savkopiata ĝis vi " +"inkludi ĝin ankaŭ." -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "" +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Ĉu vi volas inkluzivi la celon de la simbola ligilo anstataŭe?" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "" +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Inkludi dosierojn" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Agordoj" +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Inkludi dosierujojn" -#: ../../qt/settingsdialog.py:615 +#: qt/manageprofiles/tab_options.py:39 msgid "Enable notifications" msgstr "Aktivigi sciigojn" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Malvalidigi savkopikreado kiam ruli je baterio" -#: ../../qt/settingsdialog.py:621 +#: qt/manageprofiles/tab_options.py:49 msgid "Power status not available from system" -msgstr "" +msgstr "Kurentstato ne estas havebla de la sistemo" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "" +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Ne ruli plurajn savkopiojn samtempe" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_options.py:56 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Aliaj savkopioj estos ŝtopita ĝis la aktuala savkopio estas kreita. Tio " +"estas malloka opcio. Do ĝi afekcias ĉiujn profilojn por tiu uzanto. Sed vi " +"devas ankaŭ aktivigi tion por ĉiujn aliaj uzanto." -#: ../../qt/settingsdialog.py:630 +#: qt/manageprofiles/tab_options.py:63 msgid "Backup replaced files on restore" -msgstr "" +msgstr "Anstataŭigi dosierojn dum restaŭri" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Kontinui je eraroj (savi nekompletajn savkopiojn)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Uzi kontrolsumo por detekti ŝanĝojn" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Krei novan savkopion eĉ se nenio ŝanĝis." -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Averti se la kvanto de libera loko malpliiĝi ol" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Vidigas averton se la kvanto de libera loko je la savkopi-cela disko estas " +"malpli ol la specifita valoro." -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Se la politiko de Forigado & Konservado estas ŝaltita kaj malnovaj savkopioj" +" estas forigata pro la havebla libera loko, tio valoro ne povas esti malpli " +"ol la valoro agordita per la politiko." -#: ../../qt/settingsdialog.py:653 +#: qt/manageprofiles/tab_options.py:122 msgid "Log Level:" -msgstr "" +msgstr "Nivelo de protokolado:" -#: ../../qt/settingsdialog.py:658 +#: qt/manageprofiles/tab_options.py:185 msgid "None" msgstr "Neniu" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "uzant-manlibron" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"La sekvantaj reguloj estas prilaborata de la supro al la malsupro. Pli " +"malfruaj reguloj anstataŭigas pli fruajn. Vidu la {manual_link} por detaloj " +"kaj ekzemploj." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Malfermi uzant-manlibron en la foliumilo." -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Ne forigi plej novan savkopion." -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "La plej nova savkopio estas konservata ĉiam." -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Tio konduto ne povas esti ŝanĝita." -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Ne forigi nomitajn savkopiojn." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Savkopioj, kio estas nomita krome havi la ordinaran tempindikon, estos " +"konservataj ĉiam kaj ne estos forigataj." -#: ../../qt/settingsdialog.py:714 -msgid "on local machine" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Jaro(j)" -#: ../../qt/settingsdialog.py:721 -msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Forigi savkopiojn plej malnova ol" -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Kompletaj tagoj. La aktuala tago estas ignorata." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Kalendaraj semajnoj kun lundo kiel unua tago. La aktuala semajno estas " +"ignorata." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Periodo da 12 monatoj. La aktuala monato estas ignorata." -#: ../../qt/settingsdialog.py:774 -msgid "Preserve ACL" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Politiko de konservado" -#: ../../qt/settingsdialog.py:787 -msgid "Preserve extended attributes (xattr)" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Ruli fone je la foriga gastiganto." -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"La politiko de konservado rulos senpere sur la fora maŝino, ne loke. La " +"komandoj \"bash\", \"screen\" kaj \"flock\" devas esti instalitaj kaj " +"haveblaj sur la fora maŝino." -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Se elektita, Back In Time testos la fora maŝino unue." -#: ../../qt/settingsdialog.py:839 -msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "La tagoj estas kalkulataj ekde hodiaŭ." -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Savi ĉiujn savkopiojn de la lasta(j)" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "tago(j)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Savi lastan savkopion po tago je la lasta(j)" + +#: qt/manageprofiles/tab_remove_retention.py:344 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"La semajnoj estas kalkulataj ekde la aktuala semajno. Semajnoj estas " +"rigardataj kiel starti kun lundo." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Savi lastan savkopion po semajno je la lasta(j)" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "semajno(j)." -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_remove_retention.py:357 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"The months are counted as calendar months starting with the current month." msgstr "" +"La monatoj estas kalkulataj kiel kalendaraj monatoj ekde la aktuala monato." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Savi lastan savkopion po monato je la lasta(j)" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "monato(j)." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_remove_retention.py:370 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." -msgstr "" +"The years are counted as calendar years starting with the current year." +msgstr "Jaroj estas kalkulataj kiel kalendaraj jaroj ekde la aktuala jaro." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Savi lastan savkopion po ano de la lasta(j)" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "ĉiuj jaroj." -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… la kvanto de libera loko estas malpli ol" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… la kvanto de liberaj indeksnodoj estas malpli ol" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Malnovaj savkopioj estas forigitaj se …" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "" +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "Aŭtentigo estas necesa por ruli Back In Time kiel administranto." -#: ../../qt/settingsdialog.py:1201 +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" +"Aŭtentigo estas necesa por ŝanĝi rezervhorarojn ekigitajn de konektoj de " +"eksteraj memoriloj." -#: ../../qt/settingsdialog.py:1237 -msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" -msgstr "" +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Klavkombinoj" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Lokoj" -#: ../../qt/settingsdialog.py:1360 -msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" -msgstr "" +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Dosiersistemo" -#: ../../qt/settingsdialog.py:1376 -#, python-format -msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" -msgstr "" +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Savkopio-dosierujoj" -#: ../../qt/settingsdialog.py:1384 -msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" -msgstr "" +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profilo: \"{profile_name}\"" -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "" +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profilo: \"{profile_name}\" (per uzanto \"{desktop_user}\")" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "" +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Vidi lastan protokolon" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "" +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Komencu {appname}" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "" +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Laborante…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "manpaĝo: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importi agordon" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Neniu dosierujo elektita" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importi" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Serĉanta…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Elekti la savkopi-dosierujon de kie la agord-dosiero devus esti importota. " +"La vojo eble aspektas tiel: {samplePath}" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Se la dosierujo estas je ekstera aŭ fora disko, ĝi devas esti mane surmetita" +" antaŭe." -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "" +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Skani ree" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Montri kaŝitajn dosierujojn" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "" +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Vidigi/malvidigi kaŝitajn dosierujojn (Ctrl+H)" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Neniu agordo trovita en ĉi tiu dosierujo" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "" +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Serĉado kompletas." -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr "" +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Vidigi tutan protokolon" -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Retronombrado al Sistemfermo" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "" +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "La savkopio estas finita." -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "" +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Nuligi Sistemfermon" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "" +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Sistemfermi Nun" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "" +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "La sistemo fermos je {n} sekundo." +msgstr[1] "La sistemo fermos je {n} sekundoj." -#: ../../qt/snapshotsdialog.py:60 +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Opcioj de kompari savkopiojn" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Komando:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametroj:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "" +msgstr "Uzi %1 kaj %1 por vojparametroj" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Bonvolu agordi diff-komandon aŭ premi \"Ĉesi\"." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"Ne eblas trovi la komandon \"{cmd}\" je tio sistemo. Bonvolu provi ian alion" +" aŭ premi \"Ĉesi\"." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Neniom da parametroj agordita por la diff-komando. Uzi defaŭltan valoron " +"\"{params}\"." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Savkopioj" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Sole malsamaj savkopioj" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Sole listigi savkopiojn samajn al:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Kompleta kontrolo (pli precize, sed pli malrapide)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Forigi" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Elekti ĉion" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Kompari" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Iri al" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "" - -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opcioj" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "Ne eblas kompari savkopion kun mem, ĉar la komparado estus senutila." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Ĉu vi vere volas forigi {file_or_dir} en savkopio {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Ĉu vi vere volas forigi {file_or_dir} en {count} savkopioj?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "AVERTO: Ĉi tio ne povas esti nuligita." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Ekskluzivi {path} de estontaj savkopioj?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Ĉef-reĝimo" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" +"Back In Time nuntempe rulas je la privilegioj de la ĉej-uzanto (aliro al la " +"tuta sistemo)" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Hodiaŭ" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Hieraŭ" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:76 +msgid "This week" +msgstr "Nuna semajno" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Lasta semajno" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Lasta kontrolo {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Tio NE estas savkopion, sed viva vido de viaj lokaj dosieroj." diff --git a/common/po/es.po b/common/po/es.po index 92fea30bb..65c6809d5 100644 --- a/common/po/es.po +++ b/common/po/es.po @@ -1,1682 +1,2633 @@ -# Spanish translation of Backintime. -# Copyright (C) 2008-2009 Oprea Dan -# This file is distributed under the same license as the Backintime package. -# Francisco M. Garcia Claramonte , 2008-2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Francisco Manuel García Claramonte +# SPDX-FileCopyrightText: © Mauricio J. Adonis C. +# SPDX-FileCopyrightText: © Adolfo Jayme Barrientos , 2025 +# SPDX-FileCopyrightText: © Pablo Huguet # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2018-02-13 23:00+0000\n" -"Last-Translator: A Roach \n" -"Language-Team: Spanish/Spain \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-06 12:48+0000\n" +"Last-Translator: gallegonovato \n" +"Language-Team: Spanish \n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Día(s)" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Año(s)" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/config.py:1474 +#: common/bitlicense.py:43 +#, python-brace-format msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" -"No se pudo encontrar crontab.\n" -"¿Está seguro de que cron está instalado?\n" -"Si no, debe desactivar todas las copias automáticas." +"Todas las licencias usadas en este proyecto se encuentran en el directorio " +"{dir_link}. Para extraer información de licencia y derechos de autor por " +"archivo a través de metadatos SPDX, consulte {readme_link}." -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Tomar instantánea" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Aviso" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Finalizado" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Perfil principal" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Terminando" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Local (cifrado EncFS)" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Borrado inteligente" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (cifrado EncFS)" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Nombre de la instantánea" +#: common/config.py:237 +msgid "Local" +msgstr "Local" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Eliminar instantánea" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Cifrado localmente" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Mostrar archivos ocultos" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Cifrado" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Accesos directos" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Hecho, no se necesita una copia de respaldo" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Clave privada SSH" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Administrador" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Perfil: «{name}»" -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"¿Está seguro de que desea eliminar la instantánea:\n" -"%s?" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "El directorio de la copia de seguridad no es válido." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Dónde guardar las instantáneas:" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Debe seleccionarse al menos un directorio para la copia de seguridad." -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Añadir archivo" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Directorio: {path}" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Añadir carpeta" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "" +"Este directorio no puede incluirse en la copia de seguridad ya que es parte " +"del destino de la misma." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "No eliminar las instantáneas con nombre" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" +"El valor de «Eliminar la copia de seguridad más antigua si el espacio libre " +"es inferior a» ({val_one}) debe ser inferior o igual al umbral de «Advertir " +"si el espacio libre en disco es inferior a» ({val_two})." -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Activar notificaciones" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" +"Ajuste las configuraciones de modo que el límite de eliminaciones de backups" +" no supere el límite de aviso." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Excluir archivo" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "La programación de Udev no funciona con el modo {mode}" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Opciones al mostrar diferencias (Diff)" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Error al escribir nuevo crontab." -#: ../../qt/snapshotsdialog.py:68 -msgid "Use %1 and %2 for path parameters" -msgstr "Usar %1 y %2 para los parámetros de ruta" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron no se está ejecutando pese a que la orden crontab está disponible. Los " +"trabajos de respaldo programados no se ejecutarán. Es posible que Cron esté " +"instalado pero no activado. Intente ejecutar las órdenes «systemctl enable " +"cron» y «systemctl start cron», o consulte los canales de asistencia de la " +"distribución GNU/Linux que usa para obtener ayuda." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Error al guardar la configuración" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Se produjo un fallo al cargar la configuración" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "El perfil «{name}» ya existe." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "No puede eliminar el último perfil." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "No se puede montar «{command}»" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "No se encuentra la configuración del directorio cifrado." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "¿Quiere crear un directorio cifrado nuevo?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Cancelar" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diferencias" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Por favor, vuelve a introducir la contraseña de EncFS para confirmar." -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Error al guardar la configuración: %s" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "Las contraseñas de EncFS no coinciden." -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Se produjo un fallo al cargar la configuración: %s" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Tomar instantánea" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "El perfil «%s» ya existe." +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "No se puede inicializar la ruta cifrada «{command}»" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "No puede quitar el último perfil." +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "No se puede desmontar {mountprocess} de {mountpoint}." -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Desactivado" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"No se encontró la orden {command} . Instálela (por ejemplo, mediante " +"«{installcommand}»)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "En cada arranque/reinicio" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "El punto de montaje {mntpoint} no está vacío." -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Cada cinco minutos" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Introduzca la contraseña para el perfil {mode} «{profile}»:" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Cada diez minutos" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"No se pudo instalar la regla Udev para el perfil {profile_id}. El servicio " +"DBus «{dbus_interface}» no estaba disponible." -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Cada 30 minutos" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "No se pudo encontrar el UUID de «{path}»" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Cada hora" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "Falló" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Cada 2 horas" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Restaurar permisos" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Cada 4 horas" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Finalizado" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Cada 6 horas" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Las entradas siguientes de la lista de inclusiones no tienen correspondencia" +" de archivo o carpeta en la fuente del respaldo:" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Cada 12 horas" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Aplazando la copia de respaldo mientras se usa la batería" -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Horas personalizadas" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "No se puede encontrar el directorio de respaldos." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Cada día" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Si está en una unidad extraíble, conéctela." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Repetidamente" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Esperando {n} segundo." +msgstr[1] "Esperando {n} segundos." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Cuando la unidad sea conectada (udev)" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "No se pudo crear el respaldo {snapshot_id}." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Cada semana" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Aguarde un momento. Finalizando…" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Cada mes" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "No se puede crear el directorio." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Semana(s)" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Guardando archivo de configuración…" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Hora(s)" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Guardando permisos…" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Mes(es)" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "" +"Se ha encontrado el respaldo incompleto {snapshot_id} que puede continuarse." -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " ¡EXPERIMENTAL!" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "" +"Quitando el directorio {snapshot_id} incompleto de la ejecución pasada" -#: ../../common/config.py:129 -msgid "Local" -msgstr "Local" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "No se puede eliminar la carpeta" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Creando respaldo" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Llave privada SSH" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Exito" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Cifrado local" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "La transferencia fue parcial por un error" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Encriptación" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "" +"Transferencia parcial debido a la desaparición de archivos fuente (consulte " +"«man rsync»)" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "Encriptado SSH" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "«rsync» finalizó con el código de salida {exit_code}" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Por defecto" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Consulte «man rsync» para más detalles" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "" +"Los códigos de salida negativos de rsync son números de señales; consulte " +"«kill -l» y «man kill»" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Nada ha cambiado; no hace falta un respaldo nuevo" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "No se puede renombrar {new_path} a {path}." -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Aplicando reglas para eliminar copias de seguridad antiguas" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Aplicación de la política de retención" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Intentar mantener el espacio libre mínimo" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Intentando mantener un mínimo de {perc} inodos libres" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Ahora" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "No se puede montar {sshfs}" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent no se encuentra. Asegúrese de que está instalado." -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "" +"No se pudo desbloquear la clave privada SSH. Contraseña incorrecta o " +"contraseña inaccesible para cron." -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "La ruta remota existe pero no es un directorio." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Perfil principal" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "La ruta remota no admite escritura." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Perfil: «%s»" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "La ruta remota no es ejecutable." -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "La carpeta de instantáneas no es válida." +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "No se ha podido crear la ruta remota." -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Debe seleccionar al menos una carpeta para copiar." +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "El anfitrión remoto {host} no admite {command}" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "No puede incluir una carpeta de copias de seguridad." +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "" +"Verificando comandos en el host {host} que devolvió un error desconocido" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "No puede incluir una subcarpeta de copias de seguridad." +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "El anfitrión remoto {host} no admite enlaces duros" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s no es una carpeta." +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Copiar clave pública SSH «{pubkey}» en el anfitrión remoto «{host}»." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Servidor/Usuario/ID-Perfil no pueden estar vacíos!" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Introduzca una contraseña para «{user}»." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format +#: common/tools.py:382 +#, python-brace-format msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"No se puede escribir en: %s\n" -"¿Está seguro de que tiene permiso de escritura?" +"El sistema de archivos de destino para {path} está formateado con NTFS, que " +"tiene incompatibilidades conocidas con los sistemas de archivos de estilo " +"Unix." + +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} no es un directorio válido." -#: ../../common/config.py:402 -#, python-format +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "No se pudo crear el directorio siguiente:" + +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "El acceso de escritura puede estar restringido." + +#: common/tools.py:471 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" -"El sistema de ficheros de destino para la ruta '%(path)s' está formateada " -"como FAT, la cual no permite enlaces duros. Por favor, use un sistema de " -"ficheros nativo de Linux." +"El sistema de archivos de destino para {path} está formateado con FAT que no" +" admite enlaces físicos. Use un sistema de archivos nativo de GNU/Linux." -#: ../../common/config.py:407 -#, python-format +#: common/tools.py:482 +#, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" -"El sistema de ficheros de destino para la ruta '%(path)s' es un recurso " -"compartido SMB. Por favor, asegúrese qe que el servidor remoto soporta " -"enlaces simbólicos o active '%(copyLinks)s' en '%(expertOptions)s'." +"El sistema de archivos de destino para {path} es un recurso compartido " +"montado a través de SMB. Asegúrese de que el servidor SMB remoto admite " +"enlaces simbólicos o active «{copyLinks}» en «{expertOptions}»." -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 +#: common/tools.py:486 msgid "Copy links (dereference symbolic links)" -msgstr "Copiar vínculos (desarbitrar vínculos simbólicos)" +msgstr "Copiar enlaces (desreferenciar enlaces simbólicos)" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 +#: common/tools.py:487 msgid "Expert Options" msgstr "Opciones avanzadas" -#: ../../common/config.py:413 -#, python-format +#: common/tools.py:491 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"El sistema de archivos de destino para {path} es un recurso compartido " +"montado a través de sshfs. Sshfs no admite enlaces duros. Use el modo «SSH» " +"en su lugar." -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "Error al escribir nuevo crontab" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "La creación del archivo falló en este directorio:" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"No se pudo instalar la regla Udev para el perfil %(profile_id)s. El servicio " -"DBus '%(dbus_interface)s' no está disponible" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Acerca de Back In Time" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Programar udev no funciona con el modo %s" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Derechos de autor:" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "No se pudo encontrar UUID para «%s»" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Autoría:" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Traducción:" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" -"No se pudo montar '%(command)s':\n" -"\n" -"%(error)s" +"Francisco Manuel García Claramonte \n" +"Mauricio J. Adonis C. \n" +"Adolfo Jayme Barrientos , 2025\n" +"Pablo Huguet " -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "No se ha encontrado la configuración para el directorio encriptado" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Los créditos de traducción no están disponibles en este idioma." -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "este enlace" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -"\n" -"¿Crear un nuevo directorio encriptado?" +"Visite {thislink} para obtener los créditos de traducción de todos los " +"idiomas." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Cancelar" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Sitio web del proyecto" + +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Manual de utilización" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Por favor, confirme la contraseña" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "" +"Abrir el manual del usuario en el navegador (local si está disponible, de lo" +" contrario, en línea)" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "La contraseña no coincide" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Versión{BOLDEND}: {version}" -#: ../../common/encfstools.py:161 +#: qt/app.py:332 +#, python-brace-format msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -"encfs version 1.7.2 y anteriores tienen un error al usar la opcion --" -"reverse. Por favor actualice encfs" +"{app_name} parece estar ejecutándose por primera vez porque no se encuentra " +"ninguna configuración." -#: ../../common/mount.py:415 -#, python-format +#: qt/app.py:337 msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." +"Import an existing configuration from a backup location or another computer?" msgstr "" -"Una colisión Hash ha ocurrido en hash_id%s. Incremente el valor global " -"hash_collision y trate de nuevo." +"¿Importar una configuración existente desde un respaldo o desde otro " +"ordenador?" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "No se pudo desmontar %(proc)s de %(mountpoint)s" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Luego, pulse en Aceptar." -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Crear un respaldo" -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" +"Utiliza el tiempo de modificación y el tamaño para detectar cambios en los " +"archivos." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "El punto de montaje %s está en uso" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Crear un respaldo (modo suma de verificación)" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Utiliza sumas de verificación para detectar cambios en los archivos." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Perfil '%(profile)s': Ingrese clave para %(mode)s: " +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Pausar el proceso de respaldo" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "Este log ha sido decodificado con busqueda automática de patrón.\n" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Reanudar el proceso de respaldo" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "Falló" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Detener el proceso de respaldo" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Recuperar permisos" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Actualizar lista de respaldos" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Dar nombre a respaldo" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" -"No se pudo encontrar la carpeta de instantáneas.\n" -"Si está en una unidad extraíble, por favor, conéctela." +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Quitar respaldo" -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Esperando %s segundo." -msgstr[1] "Esperando %s segundos." +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Abrir registro de respaldos" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Fallo al crear la instantánea %s" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Ver el registro de la copia de seguridad seleccionada." -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "No se puede crear carpeta: %s" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Ver el registro de la copia de seguridad seleccionada" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Guardando el archivo de configuración..." +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Ver el registro del respaldo más reciente." -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Salvando permisos..." +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Perfil principal…" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Editar user-callback" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/app.py:532 +msgid "Shutdown" +msgstr "Apagar" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Apagar el sistema al terminar el respaldo." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "No se puede eliminar la carpeta: %s" +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Configurar idioma…" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Tomar instantánea" +#: qt/app.py:540 +msgid "Exit" +msgstr "Salir" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "Pagina del manual: Back In Time" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "No se puede renombrar %(new_path)s a %(path)s" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Muestra la página de manual sobre Back In Time (backintime)" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Eliminar instantáneas antiguas" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "Página del manual: Archivo de configuración de los perfiles" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Intentar mantener el espacio libre mínimo" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Muestra la página del manual de usuario sobre la configuración de los " +"perfiles (backintime-config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Abrir en el navegador el sitio web de Back In Time" + +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "Novedades" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" +"Abrir el registro de cambios (localmente si está disponible, de lo contrario" +" desde la web)" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "¡CON ERRORES!" +#: qt/app.py:573 +msgid "FAQ" +msgstr "Preguntas frecuentes" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Ahora" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Abrir en el navegador las preguntas frecuentes" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "No se puede montar %s" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "Hacer preguntas" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "Informar de un problema" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/app.py:584 +msgid "Translation" +msgstr "Traducción" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." msgstr "" +"Muestra nuevamente el mensaje sobre la participación en la traducción." -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s no se ha encontrado en ssh_known_hosts" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Transición de cifrado (EncFS)" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" -"La ruta remota existe, pero no es un directorio:\n" -" %s" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Muestra nuevamente el mensaje sobre la eliminación de EncFS." -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" -"No tiene permisos de escritura en la ruta remota:\n" -" %s" +#: qt/app.py:599 +msgid "About" +msgstr "Acerca de" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" -"No tiene permisos de ejecución en la ruta remota:\n" -" %s" +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Restaurar" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." msgstr "" -"No se puede crear la ruta remota:\n" -" %s" +"Restaura los archivos o directorios seleccionados a su ubicación original." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Restaurar en…" + +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." msgstr "" +"Restaura los archivos o directorios seleccionados a una nueva ubicación." -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/app.py:615 msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Restaura el directorio actual y todo su contenido a la ubicación original." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/app.py:621 msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" +"Restaura el directorio actual y todo su contenido a una nueva ubicación." -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "Arriba" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Mostrar archivos ocultos" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Comparar respaldos…" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Utilizar la suma de verificación (checksum) para detectar cambios" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Candidata para publicación" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." msgstr "" +"Muestra nuevamente el mensaje sobre esta versión candidata para publicación." -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Copia de respaldo" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Actualizar la lista de instantáneas" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Restaurar" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Ver registro de instantáneas" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Ayuda" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Ver último registro" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Icono del área de notificaciones" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Preferencias" +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automático" -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" +#: qt/app.py:784 +msgid "Light icon" +msgstr "Icono claro" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Icono oscuro" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "Salir" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Solamente iconos" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Ayuda" +#: qt/app.py:811 +msgid "Text only" +msgstr "Solamente texto" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Texto bajo los iconos" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Sitio web" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Texto junto a los iconos" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Este directorio no existe\n" +"en el respaldo seleccionado." -#: ../../qt/app.py:167 -msgid "FAQ" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"Este directorio no existe\n" +"en el respaldo seleccionado." -#: ../../qt/app.py:169 -msgid "Ask a question" +#: qt/app.py:995 +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Si esta ventana está cerrada, Back In Time no podrá apagar su sistema cuando" +" la copia de seguridad haya terminado." -#: ../../qt/app.py:171 -msgid "Report a bug" -msgstr "" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "¿Quiere cerrar la ventana de todos modos?" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "Acerca de" +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Hecho; no se necesita una copia de respaldo" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Arriba" +#: qt/app.py:1260 +msgid "Working:" +msgstr "Trabajando:" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Restaurar" +#: qt/app.py:1267 +msgid "Working" +msgstr "En curso" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Error" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Recuperar en ..." +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "Enviado:" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "Velocidad:" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "Tiempo restante estimado:" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Copia de seguridad:" -#: ../../qt/app.py:249 -#, python-format +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Restaurar {path}" + +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Restaurar {path} en…" + +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Hola\n" +"Ya has utilizado Back In Time en el idioma {language} unas cuantas veces.\n" +"La traducción de su versión instalada de Back In Time al {language} está {perc} completa. Independientemente de su nivel de conocimientos técnicos, puede contribuir a la traducción y, por tanto, al propio Back In Time.\n" +"Visite {translation_platform_url} si desea contribuir. Para más ayuda y preguntas, visite el {back_in_time_project_website}.\n" +"Le pedimos disculpas por la interrupción, y este mensaje no se volverá a mostrar. Este diálogo está disponible en cualquier momento a través del menú de ayuda.\n" +"Su equipo Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "plataforma de traducciones" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Sitio web" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Instantáneas" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "La traducción" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "En el fediverso en Mastodon: {link_and_label}." -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Correo electrónico a {link_and_label}." -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Lista de correo {link_and_label}." -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} en la página web del proyecto." -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Informar de un fallo" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" -msgstr "" +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "De forma alternativa, puede usar otro canal de su elección." -#: ../../qt/app.py:492 +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" -msgstr "" -"No se pudo encontrar la carpeta de instantáneas.\n" -"Si se encuentra en un dispositivo extraíble, por favor, conéctelo y pulse OK" - -#: ../../qt/app.py:527 +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Esta versión de Back In Time es preliminar y está destinada principalmente a realizar pruebas de estabilidad para preparar la próxima versión oficial.\n" +"No se recogen datos de usuario ni telemetría. No obstante, el equipo de Back In Time está muy interesado en saber si se le da uso a la versión preliminar y si merece la pena seguir brindándolas.\n" +"Por ello, el equipo le pide que nos comunique brevemente si ha probado esta versión, aunque no haya encontrado ningún problema. Incluso una prueba rápida de unos minutos nos ayudaría mucho.\n" +"Tiene a su disposición las siguientes opciones de contacto:\n" +"{contact_list}\n" +"En esta versión, este mensaje no se volverá a mostrar pero se podrá acceder a él en cualquier momento a través del menú Ayuda.\n" +"¡Gracias por su apoyo y por ayudarnos a mejorar Back In Time!\n" +"El equipo de Back In Time" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Solo {free} de almacenamiento libre en el destino, lo cual es menor al " +"límite configurado de {threshold}." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "Trabajando:" - -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Error:" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "¿Proceder con la copia de seguridad?" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" +"Desaparecerán todos los archivos más recientes en {path}. ¿Quiere continuar?" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Desaparecerán todos los archivos más recientes del directorio original. " +"¿Quiere continuar?" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" +#: qt/app.py:1875 +#, python-brace-format +msgid "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Atención{BOLDEND}: eliminar archivos en la raíz del sistema de " +"archivos podría hacer que todo el sistema deje de funcionar." -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Global" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Nombre del respaldo" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Carpeta Personal" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "¿Quiere quitar este respaldo?" +msgstr[1] "¿Quiere quitar estos respaldos?" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Carpeta de copias de seguridad" - -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" +"La configuración de idioma solo surte efecto después de reiniciar Back In " +"Time." -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Copias de seguridad versionadas (root)" -#: ../../qt/app.py:1038 +#: qt/backintime-qt-root.desktop:23 msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" - -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" +msgstr "GUI para copias de seguridad con menor uso de espacio (modo root)" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Copias de seguridad versionadas" -#: ../../qt/app.py:1083 -#, python-format -msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Interfaz gráfica de usuario fácil de usar para copias de seguridad " +"versionadas que reduce el uso del disco" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Pregunta" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Crea copias de seguridad con el sufijo {suffix} antes de sobrescribir o " +"eliminar elementos locales." -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Antes de restaurar, las versiones más recientes de los archivos se " +"renombrarán añadiéndoles el sufijo {suffix}. Estos archivos se pueden " +"eliminar con el siguiente comando:" -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:94 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Solo restaura los elementos que no existen o que son más recientes que los " +"del destino. Usando la opción «{rsync_example}»." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Ver el contenido actual del disco" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Instantánea: %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Ver la instantánea hecha el %s" - -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Recuperar '%s'" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Eliminar los elementos más nuevos del directorio original." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Recuperar '%s' en" - -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Restaurar los archivos o directorios seleccionados a la ubicación original y" +" eliminar los archivos o directorios que no se encuentran en la copia de " +"seguridad. Tenga mucho cuidado porque esto eliminará archivos y directorios " +"que se excluyeron durante la creación de la copia de seguridad." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "¿Realmente quiere restaurar este elemento en el directorio nuevo?" +msgstr[1] "" +"¿Realmente quiere restaurar estos elementos en el directorio nuevo?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "¿Realmente quiere restaurar este elemento?" +msgstr[1] "¿Realmente quiere restaurar estos elementos?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Retrollamada-usuario: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"El user-callback script debe incluir una shebang en la primera linea (e.g. " +"{example})." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Mostrar/ocultar archivos y directorios ocultos (Ctrl+H)" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Añadir a Incluir" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Añadir a Excluir" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Seleccione entre los elementos de uso común para agregarlos a la lista de " +"exclusión." +msgstr[1] "" +"Seleccione entre los elementos de uso común para agregarlos a la lista de " +"exclusión." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Seleccione entre los elementos de uso común para agregarlos a la lista de " +"exclusión." +msgstr[1] "" +"Seleccione entre los elementos de uso común para agregarlos a la lista de " +"exclusión." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Configurar el idioma" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Traducido: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Predeterminado del sistema" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Utilizar el idioma del sistema operativo." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Vista de registros de respaldo" + +#: qt/logviewdialog.py:63 msgid "Last Log View" -msgstr "" +msgstr "Ver el último registro" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Perfil:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Respaldos:" + +#: qt/logviewdialog.py:93 msgid "Filter:" -msgstr "Filtro:" +msgstr "Filtrar:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Error, [I] Información, [C] Cambiar" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "descodificar rutas" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Todo" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Cambios" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Errores" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Cambios" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Información" +msgstr[1] "Informaciones" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informaciones" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "Fallos en las transferencias rsync (experimental)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Error, [I] Información, [C] Cambiar" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Gestionar perfiles" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Editar" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Añadir" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Quitar" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&General" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Incluir" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Excluir" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Elimina&r & retención" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Trabajando..." +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Opciones" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Hoy" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "A&justes avanzados" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Ayer" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Restablecer configuración" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Esta semana" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Perfil nuevo" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Última semana" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Cambiar nombre de perfil" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "¿Quiere eliminar el perfil «{name}»?" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Copiar enlaces simbólicos como archivos" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Copiar enlaces simbólicos como archivos o directorios reales en la copia de " +"seguridad. Seleccione si desea copiar todos los enlaces o solo los que " +"apuntan fuera del origen." -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Editar" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Esta opción puede aumentar el tamaño de la copia de seguridad." -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Deshabilitado por defecto." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Todos los enlaces simbólicos se reemplazan con los archivos o directorios " +"reales a los que apuntan. Esto aumenta el tamaño de la copia de seguridad y " +"puede almacenar los mismos archivos varias veces." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Usa 'rsync --copy-links'." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "General" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Solo externa" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "" - -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"Solo se copian como archivos los enlaces que apuntan fuera de la fuente de " +"la copia de seguridad. Esto aumenta el tamaño de la copia de seguridad y " +"puede almacenar los mismos archivos varias veces." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Usa 'rsync --copy-unsafe-links'." -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Archivos temporales del editor y de Office" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Host:" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Archivos de respaldo de Emacs" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Archivos de autoguardado de Emacs" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Usuario:" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Archivos de intercambio de Vim" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Archivos temporales de Microsoft Office" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice y otros editores de OpenDocument bloquean archivos" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniaturas e imágenes temporales" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "Caché de miniaturas en GNU/Linux y otros sistemas operativos Unixoid" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Base de datos de miniaturas en Windows" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Directorio de metadatos en MacOS" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Application-specific bloqueadas" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Archivo de bloqueo de la aplicación Discord" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Avanzado" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Archivo de bloqueo de sesión de Discord" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Archivo de bloqueo de Mozilla Firefox y Thunderbird" + +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Cachés y directorios temporales" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Caché de la aplicación del usuario" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Directorio temporal del sistema" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Caché de paquetes para distribuciones GNU/Linux basadas en Debian" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Repositorio de aplicaciones y tiempo de ejecución de Flatpak" + +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Directorios temporales que almacenan datos volátiles" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Información del Kernel y del proceso" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Información del dispositivo y otro hardware (interfaz sysfs)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Nodos de dispositivo" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Archivos del sistema de tiempo de ejecución" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Otras no-persistentes" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Lista de sistemas de archivos montados actualmente" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Archivo de intercambio del sistema (memoria virtual)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "Punto de montaje del sistema de archivos virtual de GNOME" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Objetos del sistema de archivos recuperados" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Misceláneas" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Directorio de metadatos en Microsoft Windows" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Papelera de reciclaje del usuario" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Archivos de la copia de seguridad del sistema" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Excluir sugerencias" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" +"Seleccione elementos de uso común para agregarlos a las exclusiones de " +"respaldo." + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Por defecto" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Restablecer a la selección predefinida" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Tareas programadas" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Día:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "Día de la semana" +msgstr "Día laborable:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Hora:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Tiempo:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" +msgstr "Horas:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "Cada hora" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minutos:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" +"Ejecuta Back In Time tan pronto como se conecte la unidad (solo una vez cada" +" X días). Aparecerá un aviso de contraseña de sudo." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Ejecuta Back In Time repetidamente. Esto es útil si el equipo no se está " +"ejecutando regularmente." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" +msgstr "Cada:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Activar el registro de mensajes de depuración" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Escribe mensajes de nivel de depuración en el registro del sistema mediante " +"«--debug»." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Precaución: utilícelo solo temporalmente para diagnósticos, ya que genera " +"una gran cantidad de salida." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Incluir" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Desactivado" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Incluir archivos y carpetas" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "En cada arranque/reinicio" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Excluir" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Cada minuto" +msgstr[1] "Cada {n} minutos" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Cada hora" +msgstr[1] "Cada {n} horas" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Horario personalizado" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Excluir patrones, archivos o carpetas" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Todos los días" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Repetidamente (anacron)" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Se recomienda:" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Cuando la unidad se conecta (udev)" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Semanalmente" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Mensualmente" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Anualmente" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Hora(s)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Día(s)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Semana(s)" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Mes(es)" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Las horas personalizadas solo pueden ser una lista de horas separadas por " +"comas (por ejemplo, 8,12,18,23) o */3 para copias de respaldo periódicas " +"cada 3 horas." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Auto eliminar" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Vali olemasolev privaatvõtme fail kusagilt mujalt." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "nädal(ad)" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Loo uus ilma salafraasita SSH võti." -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Täisasukoht: {path}" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "kuu(d)" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Privaatvõti:" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Kasuta süsteemi SSH-seadistusi" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Sellega jääb võtmefail valimata. SSH ühendused sõltuvad süsteemi üldistest " +"kliendi seadistustest (nt. ~/.ssh/config)." -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Valikud" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH proksi" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Luba teated" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Host:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Kasutaja:" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Ühenduseks välise seadmega kasuta seda proksit (tuntud ka hüppeserverina). " +"Vaata „ssh“ käsu juhendist „-J“ võtit või „man ssh_config“ juhendist " +"„ProxyJump“ lõiku." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Lisateave{ENDBOLD}: Kui töörežiimiks on „SSH krüptituna“, siis " +"toimivad vaid ühe- ja kahekordsed tärnid (näiteks: {example2}). Muud " +"metamärgid ja mustrid ei ole kasutusel (näiteks: {example1}). Kuna kasutusel" +" on krüptimine EncFSiga, siis selles režiimis on failide nimed " +"ettearvamatud." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Jäta välja mustrid, failid või kaustad" + +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Lisa muster" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Lisa faile" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Lisa kaustu" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Soovitused" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "Vali näiteid tüüpilisest välistusloendist." + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Jäta välja failid, mis on suuremad, kui:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Jäta välja failid, mis on suuremad, kui {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Kui töörežiim „Täiemahuline sünkroniseerimine rsynci abil“ pole kasutusel, " +"siis see eelistus mõjutab vaid uusi faile (tegemist on failide liigutamise " +"eelistusega, mitte välistamise eelistusega). Seetõttu varem varundatud " +"suured failid jäävad varukoopiatesse ka siis, kui neid on hiljem muudetud." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Jäta välja mustri alusel" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Jäta välja järgneva mustri alusel:" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Abiteavet leiad käsuga „man rsync“ alalõigust {link}." -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Logitase:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Ava rsynci abiteabe leht" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Pole" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Välista failid" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Muudatused & Veateated" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Välista kaustad" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" +"Kuna selline muster pole režiimis „SSH krüptituna“ funktsionaalne, siis " +"eelistus on välja lülitatud." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Need eelistused on mõeldud asjatundjatele ja keerukamate kasutusjuhtumite " +"jaoks. Muuda neid ainult siis, kui täpselt tead, mida teed." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Käivita „rsync“ käsuga „{cmd}“:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "kasutades ajastamist croniga" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" +msgstr "välises seadmes" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "kui teed varukoopiat käsitsi" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" - -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Selle eelistuse kasutamiseks palun paigalda „nocache“." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "kohalikus arvutis" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" +"Croniga ajastatud tööde puhul suuna standardväljund nulli (stdout > " +"/dev/null)." -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Kui e-kirjade edastamine on seadistatud, siis cron saadab automaatselt " +"kirja, mille manuseks on croni tehtud töö väljund." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" +"Croniga ajastatud tööde puhul suuna standardviga nulli (stderr > /dev/null)." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Kui e-kirjade edastamine on seadistatud, siis cron saadab automaatselt " +"kirja, mille manuseks on croni tehtud töös tekkinud vead." + +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/sek" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Piira rsync'i ribalaiust:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "" +msgstr "Säilita pääsuloend (ACL)" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "" +msgstr "Säilita failide laiendatud metainfo (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Piirdu ühe failisüsteemiga" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Lisatingimused peavad olema jutumärkide vahel, näiteks {example}." -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Lisa rsync-ile täiendavad suvandeid" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Välises seadmes käivita iga käsu ees see prefiks." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Muutujad peavad olema tähistatud $FOO paomärkidega. See ei käi rsynci kohta." +" Seega, lisamaks rsyncile prefikseid kasuta „{example_value}“ koos " +"„{rsync_options_value}“." + +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "vaikimisi" -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" -msgstr "" +msgstr "Lisa SSH käsule eesliiteid" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Kontrolli, kas väline seade on võrgus" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Hoiatus: kui see eelistus on välja lülitatud ja väline seade pole võrgus, " +"siis võib tekkida imelikke vigu." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." msgstr "" +"Kontrolli, kas väline seade võimaldab kõikide vajalike käskude kasutamist." -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Hoiatus: kui see eelistus on välja lülitatud ja väline ei toeta kõiki " +"vajalikke käske, siis võib tekkida imelikke vigu." -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(vaikimisi: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "pole kasutusel" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "kasutusel" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Töörežiim:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Kuhu salvestame varukoopiad" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH seadistused" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Asukoht:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Võtmefail:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Salasõna" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Salvesta salasõna tarkvaralises võtmerõngas" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" +"Puhverda salasõna croni jaoks (See on ka turvaprobleem: juurkasutajal on " +"võimalik salasõna lugeda)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Täiendavad seadistused" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Varukoopiate täpne asukoht:" + +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" +"Kuna croni paigaldust ei leidu, siis ajastamine pole kasutusel. Kui soovid, " +"et varukoopiate tegemine toimuks etteantud graafiku alusel, siis paigalda " +"arvutisse cron." + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Varunduse sihtkaust ei saa jääda tühjaks." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Krüptimise salasõna ei saa jääda tühjaks." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"Kaugseadmesse logimisel tekkis viga. Tegevuse kohta on teada selline " +"veateade:" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Kui tahad kasutada salasõnata sisselogimist, siis kopeeri vastava kasutaja " +"avalik SSH-võti kaugseadmesse." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Kas jätkame SSH-võtme kopeerimisega?" + +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Avaliku SSH võtme kopeerimine ei õnnestunud. See võis juhtuda ühenduse vea " +"või õiguste puudumise tõttu." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Hosti „{host}“ autentsust pole võimalik tuvastada." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} võtme sõrmejälg on:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Palun kontrolli, et tegemist on õige sõrmejäljega. Kas sa sooviksid teda " +"lisada ka „known_hosts“ faili?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Kas tõesti muudame varukoopiate kausta?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Valitud varunduse sihtkaust pole tühi." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Krüptimise kasutamiseks peab ta olema tühi." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Vigane fail: see pole SSH privaatvõti" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"Valitud fail ({path}) tundub olema avalik SSH võti. Palun vali privaatvõtme " +"fail (selline, kus pole .pub laiendit)." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Uus profiil" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "{path} fail on juba olemas. Sellise nimega SSH-võtit ei saa luua." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Muuda profiili nime" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Uue SSH võtme loomine asukohta „{path}“ ei õnnestunud." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Oled kindel, et tahad kustuda profiili \"%s\"?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Kaasa failid ja kaustad" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"„{path}“ on sümbollink. Lingi sihtkausta või -faili ei varundata seni, kuni " +"sa pole teda kaasanud." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Kas kaasad selle asemel sümbollingi kausta?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Kaasa failid" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Kaasa kaustad" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Luba teavitused" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Kui arvuti on akutoitel, siis ära tee varukoopiaid" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Toitesüsteemi oleks pole siin arvutis tuvastatav" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Tee korraga vaid üks varukoopia" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Muude varukoopiate tegemine on seni blokeeritud, kuni hetkel töös olev " +"varukoopia on tehtud. See on rakenduse üldine eelistus ja seega mõjutab " +"selle kasutaja kõiki profiile. Aga sa pead ta aktiveerima kõikide teiste " +"kasutajate jaoks." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Taastamisel tee asendatud failidest varukoopia" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Vigade puhul jätka tööd (säilita poolikud varukoopiad)" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Muutuste tuvastamiseks kasuta kontrollsummat" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." msgstr "" +"Tee uus varukoopia hoolimata sellest, kas arvutis tekkis muutusi või mitte." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Hoiata, kui vaba andmeruumi on vähem, kui" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Rakendus kuvab hoiatuse, kui varunduse sihtkaustas on määratud mahust vähem " +"vaba ruumi." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Kui „Eemaldamise ja säilitamise“ reeglid on kasutusel ja vanad varukoopiad " +"eemaldatakse vaba andmeruumi alusel, siis see väärtus ei saa olla väiksem, " +"kui reeglites on määratud." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Logitase:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Määramata" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "kasutusjuhendis" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Järgnevaid reegleid töödeldakse suunaga ülevalt alla. Hilisemad reeglid " +"alati asendavad varasemaid ega ole varasemaist kuidagi mõjutatud. " +"Üksikasjade ja näidete teave leidub siin: {manual_link}." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Ava kasutusjuhend veebibrauseris." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Jäta välja fail" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Säilita viimane varukoopia." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Jäta välja kataloog" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Viimane ja kõige värskem varukoopia säilib igal juhul." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Kaasa fail" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Seda reeglit pole mitte kuidagi võimalik muuta." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Ära kustuta nimedega varukoopiaid." + +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Varukoopiad, millel lisaks tavapärasele ajatemplile on eraldi nimi, jäävad " +"kõikides olukordades alles ega kuulu kustutamisele." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Kaasa katloog" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Aasta(t)" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Oled kindel, et tahad muuta tõmmiste kataloogi?" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Eemalda varukoopiad, mis on vanemad kui" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Täispäevades ja tänane päev ei lähe arvesse." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Esmaspäevaga algavates kalendrinädalates ja see nädal ei lähe arvesse." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12-kuulistes ajavahemikes ja praegune kuu ei lähe arvesse." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Säilitamise reeglid" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Käivita välises seadmes taustal." -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:313 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"Säilitusreegleid järgitakse otseselt välisseadmes, mitte kohalikus seadmes. " +"Selleks peavad välisseadmes olema paigaldatud „bash“, „screen“ ja „flock“." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Kui valitud, siis Back In Time esmalt testib välisseadet." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Päevi loendame alates tänasest." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Säilita kõik varukoopiad viimase" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "päeva kestel." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Säilita viimane varukoopia iga päeva kohta viimase" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "Nädalaid loendame alates sellest nädalast. Nädal algab esmaspäevaga." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Säilita viimane varukoopia iga nädala kohta viimase" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "nädala jooksul." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Kuid loendame kalendrikuudena alates sellest kuust." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Säilita viimane varukoopia iga kuu kohta viimase" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "kuu kestel." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Aastaid loendame kalendriaastatena alates sellest aastast." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Säilita viimane varukoopia iga aasta kohta" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "läbi aastate." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… kui vaba ruumi on vähem kui" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… kui vabu indekssõlmesid on vähem kui" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Eemalda vanimad varukoopiad, kui…" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" +"Et saaksid Back In Time'i käivitada peakasutaja õigustes, pead end " +"autentima." -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" +"Kui soovid muuta varunduse ajastamist, mis käivituvad väliste andmekandjate " +"ühendamisel, siis pead end autentima." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Otseteed" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Asukohad" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Failisüsteem" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Tagavarakoopia kaustad" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profiil: „{profile_name}“" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profiil: „{profile_name}“ (kasutajalt „{desktop_user}“)" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Vaata viimast logi" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Käivita {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Töötan…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "Abiteabe leht: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Impordi seadistused" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Sa pole valinud ühtegi kausta" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Impordi" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Otsin…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Vali varukoopiate kaust, kust soovid seadistuste faili importida. Asukoht " +"võiks olla midagi sellist: {samplePath}" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Kui vastav kaust asub välisel andmekandjal või kaugseadmes, siis esmalt " +"palun haagi ta käsitsi." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Skaneeri uuesti" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Näita peidetud kaustu" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Näita/peida peidetud kaustu (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Selles kaustas ei leidunud ühtegi seadistust" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Otsing on lõppenud." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Näita tervet logi" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Pöördloendus väljalülitamiseni" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Varukoopia tegemine on lõppenud." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Katkesta väljalülitamine" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Lülita välja kohe" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Arvuti lülitub välja {n} sekundi pärast." +msgstr[1] "Arvuti lülitub välja {n} sekundi pärast." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Varukoopiate võrdlemise valikud" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Käsk:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parameetrid:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "" +msgstr "Asukoha parameetritena kasuta „%1“ ja „%2“" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Palun seadista „diff“ käsk või vajuta „Katkesta“." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"Käsku „{cmd}“ ei leidu selles seadmes. Palun proovi midagi muud või vajuta " +"„Katkesta“." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Diff käsu parameetrid puuduvad. Kasutan vaikimisi väärtust „{params}“." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Varukoopiad" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Vaid siis, kui varukoopiad erinevad" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Näita vaid varukoopiaid, mis võrduvad järgnevaga:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Põhjalik kontroll (palju täpsem, aga ka aeglasem)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Kustuta" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Vali kõik" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Erinevus" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Võrdle" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Mine" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Sa ei saa võrrelda tõmmist iseendaga" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Valikud" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Kuna võrdlus oleks siis liigne, pole võimalik teha varukoopiat iseendasse." -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Käsku ei leitud: %s" +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" +"Kas sa kindlasti soovid kustutada „{file_or_dir}“ hetktõmmises nr " +"{backup_id}?" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "" +"Kas sa kindlasti soovid kustutada „{file_or_dir}“ kokku {count}-s " +"varukoopias?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "HOIUATUS: seda tegevust ei saa tagasi pöörata." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Kas soovid välistada tulevastest varukoopiatest asukoha „{path}“?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Juurkasutaja režiim" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" +"Back In Time töötab hetkel juurkasutaja õigustes (ehk täisligipääsuga " +"sellele süsteemile)" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Täna" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Eile" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:76 +msgid "This week" +msgstr "Sel nädalal" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Eelmisel nädalal" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Viimati kontrollitud {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "See EI OLE varukoopia, vaid sinu kohaliku failisüsteemi failide tegelik " +#~ "vaade." diff --git a/common/po/eu.po b/common/po/eu.po index b553cc17f..15aba7a9c 100644 --- a/common/po/eu.po +++ b/common/po/eu.po @@ -1,1810 +1,2619 @@ -# Basque translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Alexander Gabilondo # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2019-01-01 22:04+0000\n" -"Last-Translator: Alexander Gabilondo \n" -"Language-Team: Basque \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-18 20:29+0000\n" +"Last-Translator: alexgabi \n" +"Language-Team: Basque \n" +"Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Huts egin du ezarpenak gordetzen: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Huts egin du ezarpenak kargatzen: %s" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "\"%s\" profila badago honezkero." +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" +"Proiektu honetan erabiltzen diren lizentzia guztiak {dir_link} direktorioan " +"daude. Fitxategiaren lizentzia eta copyrightari buruzko informazioa SPDX " +"metadatuak erabiliz ateratzeko, ikusi {readme_link}." -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Ezin duzu ezabatu azken profila!" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Abisua" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Ezgaituta" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Profil nagusia" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Abiatze/berrabiaratze guztietan" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Lokala (EncFS zifratua)" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "5 minuturo" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS zifratua)" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "10 minuturo" +#: common/config.py:237 +msgid "Local" +msgstr "Lokala" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "30 minuturo" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Lokalki zifratua" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Orduro" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Zifraketa" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "2 ordutan behin" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "4 ordutan behin" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH giltz pribatu" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "6 ordutan behin" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profila: \"{name}\"" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "12 ordutan behin" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Babeskopien direktorioa ez da baliozkoa." -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "ordu pertsonalizatuetan" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Gutxienez direktorio bat hautatu behar da babeskopia egiteko." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Egunero" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Direktorioa: {path}" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Behin eta berriz (anacron)" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "" +"Karpeta hau ezin da babeskopian sartu babeskopiaren helburu beraren zatia " +"baita." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Unitatea konektatzean (udev)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" +"\"Kendu babeskopiarik zaharrena, leku librea hau baino txikiagoa bada\" " +"({val_one}) balioak \"Abisatu diskoko leku librea behera badoa\" ({val_two})" +" atalasea baino txikiagoa edo berdina izan behar du." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Astero" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" +"Mesedez, doitu ezarpenak babeskopia ezabatzeko muga abisu-muga baino " +"handiagoa izan ez dadin." -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Hilero" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Programatutako Udev-a ez dabil {mode} moduarekin" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Egun(ak)" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Huts egin du crontab berria idazten." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "aste" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron ez da exekutatzen crontab agindua eskuragarri egon arren. " +"Programatutako babeskopia lanak ez dira exekutatuko. Baliteke Cron " +"instalatuta egotea baina gaituta ez egotea. Saiatu \"systemctl enable cron\"" +" eta \"systemctl start cron\" komandoak exekutatzen edo kontsultatu zure " +"GNU/Linux banaketaren laguntza kanalak." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Huts egin du ezarpenak gordetzen" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Huts egin du ezarpenak kargatzen" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "\"{name}\" profila badago honezkero." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Ezin da azken profila kendu." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "\"{command}\" ezin da muntatu" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Zifratutako karpetaren ezarpenak ez dira aurkitu." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Karpeta zifratu berri bat sortu?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Utzi" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "urtez" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Sartu berriro EncFS pasahitza berresteko." -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Ordu" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFS pasahitzak ez datoz bat." -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Hilabete" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Egin babeskopia" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " ESPERIMENTALA!" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Ezin da \"{command}\" enkriptatutako bidea abiarazi" -#: ../../common/config.py:129 -msgid "Local" -msgstr "Lokala" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Ezin da {mountprocess} desmuntatu {mountpoint}-tik." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"{command} ez da aurkitu. Mesedez instalatu (adibidez \"{installcommand}\"ren" +" bidez)" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "SSH gako pribatua" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "{mntpoint} muntatze-puntua ez dago hutsik." -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Lokala zifratua" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Idatzi {mode} \"{profile}\" profilaren pasahitza:" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Zifraketa" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Ezin izan da Udev araua instalatu {profile_id} profilarentzat. " +"'{dbus_interface}' DBus zerbitzua ez zegoen erabilgarri." -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH zifratua" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Ezin da UUID aurkitu \"{path}\"-erako" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Lehenetsia" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "HUTS EGIN DU" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Leheneratu baimenak" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Eginda" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Zerrendako sarrera hauek ez dute dagokien fitxategi edo direktoriorik " +"babeskopiaren iturburuan:" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Atzeratu babeskopia bateriaz dabilen bitartean" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Ezin da aurkitu babeskopien direktorioa." -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Unitate eramangarri batean badago konekta ezazu." -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Segundo {n} itxaroten." +msgstr[1] "{n} segundo itxaroten ." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Huts egin du {snapshot_id} babeskopia egiten." -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Mesedez, izan pazientzia. Amaitzen…" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Ezin da karpeta sortu." -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Gorde ezarpenen fitxategia…" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Baimenak gordetzen…" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Profil nagusia" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "" +"'{snapshot_id}' jarraitu daitekeen osatu gabeko babeskopia aurkitu da." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profila: \"%s\"" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Azken exekuziotik '{snapshot_id}' karpeta osatu gabea kentzen" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Babeskopien karpeta ez da baliozkoa!" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Ezin da karpeta ezabatu" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Gutxienez karpeta bat hautatu behar duzu babeskopiarako !" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Babeskopia sortzen" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Ezin duzu babeskopiaren karpeta sartu babeskopian !" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Ongi" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Ezin duzu babekopiaren azpi-karpetak sartu babeskopian !" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Transferentzia partziala errorea dela eta" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s ez da karpeta bat !" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "" +"Transferentzia partziala desagertutako iturburu-fitxategiengatik (ikus 'man " +"rsync')" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Ostalari/Erabiltzaile/Profila-ID ez da hutsik egon behar!" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "\"rsync\" amaitu da irteera-kode honekin {exit_code}" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Ezin da hor idatzi: %s\n" -"Ziur zaude idazteko baimenik baduzula?" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Ikus 'man rsync' xehetasun gehiagorako" -#: ../../common/config.py:402 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -"'%(path)s'-(r)en helburuko fitxategi-sistemaren formatua FAT da eta ez du " -"esteka gogorrik onartzen. Erabili Linux jatorriko fitxategi-sistema bat." +"Rsync irteera kode negatiboak seinale zenbakiak dira, ikusi 'kill -l' eta " +"'man kill'" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" -"'%(path)s'-(r)en helburuko fitxategi-sistema SMB-ez muntatutako partekatzea " -"da. Egiaztatu urruneko SMB zerbitzariak esteka sinbolikoak onartzen dituela " -"edo gaitu '%(copyLinks)s' '%(expertOptions)s'-en." +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Ez dago aldaketarik. Ez da babeskopia berririk egin behar" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Kopiatu estekak (erreferentzia kendu esteka sinbolikoei)" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Ezin zaio {new_path}-(r)i {path} izena jarri." -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Aukera aurreratuak" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Aplikatu babeskopia zaharrak ezabatzeko arauak" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" -"'%(path)s'-(r)en helburuko fitxategi-sistema sshfs-ez muntatutako " -"partekatzea da. sshfs-ek ez du esteka gogorrik onartzen. Erabili 'SSH' haren " -"ordez." +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Aplikatu atxikipen politika" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Ezin da aurkitu crontab.\n" -"Seguru zaude crontab instalatuta dagoela?\n" -"Crontab ez baduzu babeskopia automatiko guztiak ezgaitu beharko zenuke." +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Saiatu espazio minimoa mantentzen" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "Huts egin du crontab berria idazten." +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Saiatu gutxienez {perc} nodo libre mantentzen" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"Ezin izan da Udev araua instalatu %(profile_id)s profilarentzat. " -"'%(dbus_interface)s' DBus zerbitzua ez zegoen erabilgarri." +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Orain" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Programatutako udev-a ez dabil %s moduarekin" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Ezin da {sshfs} muntatu" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Ezin da UUID aurkitu honentzat: \"%s\"" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent ez da aurkitu. Ziurtatu instalatuta dagoela." -#: ../../common/encfstools.py:86 -#, python-format +#: common/sshtools.py:489 msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" -"'%(command)s' ezin da muntatu:\n" -"\n" -"%(error)s" +"Ezin da SSH-ren gako pribatua desblokeatu. Okerreko pasahitza edo cron-" +"entzat baliozkoa ez den pasahitza." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "Ez dira aurkitu zifratutako karpetaren ezarpenak" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Urrutiko bide-izena badago baina ez da direktorio bat." -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Sortu karpeta zifratu berri bat?" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Urrutiko bide-izenak ez dauka idazteko baimenik." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Utzi" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Urrutiko bide-izena ez da exekutagarria." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Egiaztatu pasahitza mesedez" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Ezin da sortu urrutiko bide-izena." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Pasahitzak ez datoz bat" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Urruneko {host} ostalariak ez du {command} onartzen" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" -"encfs-ren 1.7.2 bertsioak eta honen aurrekoek badute akats bat --reverse " -"aukeran. Eguneratu encfs mesedez" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Egin babeskopia" +"{host} ostalariaren egiaztatzeko komandoek errore ezezaguna itzuli dute" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" -"Hash talka gertatu da hemen: hash_id %s. Handitu hash_collision balioa eta " -"saiatu berriz." +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "{host} urrutiko ostalariak ez ditu esteka gogorrak onartzen" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "Ezin izan du %(proc)s desmontatu %(mountpoint)s-tik" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Kopiatu \"{pubkey}\" ssh-gako publikoa \"{host}\" urrutiko ostalarira." -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "Ez du %(proc)s aurkitu. Instala ezazu ad. %(install_command)s" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Sartu \"{user}\"-entzako pasahitza bat." -#: ../../common/mount.py:590 -#, python-format +#: common/tools.py:382 +#, python-brace-format msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"%(user)s ez da 'fuse' taldeko partaidea.\n" -" Exekutatu 'sudo adduser %(user)s fuse'. Aldaketak aplikatzeko atera zaitez " -"eta sartu berriro.\n" -"Begira ezazu 'man backintime' azalpen zabalagoa izateko." +"'{path}'-(r)en helburuko fitxategi-sistemaren formatua NTFS da. NTFS ezaguna" +" da Unix estikoko fitxategi-sistemekin konpatibilitate arazoak izatearren." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "%s munstatze-puntua ez dago hutsik." +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} ez da direktorio balido bat." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "Muntatze prozedura blokeatuta iraungitzean" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Ezin izan da hurrengo direktorioa sortu:" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "'%(profile)s' profila: Sartu pasahitza honentzat %(mode)s: " +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Baliteke idazteko sarbidea mugatuta egotea." -#: ../../common/snapshotlog.py:62 +#: common/tools.py:471 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" -"### Erregistro hau automatikoki bilatutako txantiloi baten bidez dekodetu " -"da\n" -"### Baldin eta bide-izenen bat ez badago dekodetua eskuz dekodetu dezakezu " -"honekin\n" - -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "HUTS EGIN DU" +"'{path}'-(r)en helburuko fitxategi-sistemaren formatua FAT da eta ez du " +"esteka gogorrik onartzen. Erabili Linux jatorriko fitxategi-sistema bat." -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Berrezarri baimenak" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." +msgstr "" +"'{path}'-(r)en helburuko fitxategi-sistema SMB-ez muntatutako partekatutako " +"karpeta da. Egiaztatu urruneko SMB zerbitzariak esteka sinbolikoak onartzen " +"dituela edo gaitu '{copyLinks}' '{expertOptions}'-en." -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Eginda" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Kopiatu estekak (erreferentzia kendu esteka sinbolikoei)" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "Atzeratu babeskopia bateriaz dabilen bitartean" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Aukera aurreratuak" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" -"Ezin du aurkitu babeskopien karpeta.\n" -"Karpeta unitate eramangarri batean badago konekta ezazu, mesedez." - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Itxaroten segundo %s" -msgstr[1] "Itxaroten %s segundo" - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Huts egi du %s babeskopia egiten!!!" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Amaitzen" - -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Ezin da %s karpeta sortu" - -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Gorde ezarpenen fitxategia" - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Baimenak gordetzen ..." - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "'%s' jarraitu daitekeen soberakina aurkitu du." - -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "Azken exekuziotik '%s' karpeta soberakina kentzen." - -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Ezin da %s karpeta ezabatu" - -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Babeskopia egiten." - -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "Ez dago aldaketarik. Ez da babeskopia berririk egin behar.." - -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Ezin jarri %(new_path)s izena honi %(path)s" - -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Ezabatze automatikoa" - -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Babeskopia zaharrak ezabatzen" - -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Saiatu espazio minimoa mantentzen" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." +msgstr "" +"'{path}'-(r)en helburuko fitxategi-sistema sshfs-ez muntatutako " +"partekatutako karpeta da. sshfs-ek ez du esteka gogorrik onartzen. Erabili " +"'SSH' haren ordez." -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Saiatu gutxienez %d%% nodo libre mantentzen." +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Ezin izan da fitxategia sortu direktorio honetan:" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "ERROREAK DAUDE!" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Back In Time aplikazioari buruz" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Orain" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Egile-eskubideak:" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Ezin da %s muntatu" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Egileak:" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" -"Ezin da SSH-ren gako pribatua desblokeatu. Okerreko pasahitza edo cron-" -"entzat baliozkoa ez den pasahitza." +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Itzultzaileak:" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" -"Huts egin du %(user)s@%(host)s-en pasahitzik gabeko autentifikazioa. " -"Begiratu 'man backintime' azalpenak ikusteko." +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Alexander Gabilondo " -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" -"Huts egin du cipher %(cipher)s ostalari honetan: %(host)s\n" -"%(err)s" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Itzultzailearen kredituak ez daude erabilgarri hizkuntza honetarako." -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "Ez da aurkitu %s ssh-known-host ostalarian." +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "esteka hau" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" -"Urrutiko bide-izena badago baina ez da direktorio bat:\n" -" %s" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "Jarraitu {thislink} hizkuntza guztien itzultzaile kredituak lortzeko." -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" -"Urrutiko bide-izena ez dauka idazteko baimenik:\n" -" %s" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Proiektuaren webgunea" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" -"Urrutiko bide-izena ez da exekutagarria:\n" -" %s" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Erabiltzailearen eskuliburua" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -"Ezin da sortu urrutiko bide-izena:\n" -" %s" +"Ireki erabiltzailearen eskuliburua nabigatzailean (tokikoa eskuragarri " +"badago, bestela lineakoa)" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "Huts egin du %s ping. Ostalaria erorita dago edo helbidea okerra da." +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Bertsioa{BOLDEND}: {version}" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -"%(host)s urrutiko ostalaria ez du '%(command)s' onartzen:\n" -"%(err)s\n" -"Begiratu 'man backintime' azalpenak ikusteko." +"{app_name} lehen aldiz exekutatzen ari dela dirudi, ez baita konfiguraziorik" +" aurkitzen." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/app.py:337 msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Import an existing configuration from a backup location or another computer?" msgstr "" -"%(host)s ostalariak txekeo komandoari errore ezezagunaz erantzun dio:\n" -" %(err)s\n" -"Begiratu 'man backintime' azalpenak ikusteko." +"Lehendik dagoen konfigurazio bat inportatu babeskopia batetik edo beste " +"ordenagailu batetik?" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "%s urrutiko ostalariak ez ditu esteka gogorrak onartzen" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Ondoren, sakatu Ados." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Sortu babeskopia bat" + +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" -"Kopiatu \"%(pubkey)s\" ssh-gako publikoa \"%(host)s\" urrutiko ostalarira.\n" -"Sartu \"%(user)s\" erabiltzailearen pasahitza:" +"Erabili denbora eta tamaina aldaketak fitxategi-aldaketak hautemateko." -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "Egin babeskopia kontrol baturarekin." +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Egin babeskopia (kontrol baturarekin)" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Erabili kontrol-batura aldaketak detektatzeko" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Erabili kontrol-batura aldaketak detektatzeko." -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" msgstr "Pausatu babeskopia prozesua" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "Laburtu babeskopia prozesua" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Babeskopia prozesuaren txosten laburra egin" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" msgstr "Gelditu babeskopia prozesua" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" +#: qt/app.py:504 +msgid "Refresh backup list" msgstr "Freskatu babeskopien zerrenda" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Babeskopiaren izena" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Izena eman babeskopiari" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Ezabatu babeskopia" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Kendu babeskopia" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" +#: qt/app.py:516 +msgid "Open backup log" msgstr "Ikusi babeskopiaren erregistroa" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Ikusi azken erregistroa" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Ikusi hautatutako babeskopiaren erregistroa." + +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Ireki azken babeskopiaren erregistroa" + +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Ikusi azken babeskopiaren erregistroa." -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Ezarpenak" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Kudeatu profilak…" -#: ../../qt/app.py:142 +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Editutatu erabiltzailearen atzeradeia" + +#: qt/app.py:532 msgid "Shutdown" msgstr "Itzali" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/app.py:534 +msgid "Shut down system after backup has finished." msgstr "Itzali sistema babeskopia bukatu ondoren." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Konfiguratu hizkuntza…" + +#: qt/app.py:540 msgid "Exit" msgstr "Irten" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Laguntza" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man orria: Back In &Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Config fitxategiaren laguntza" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Back In Time-ren man orria erakusten du (backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Webgunea" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man orria: profilen konfigurazio fitxategia" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Konfigurazio fitxategien gaineko man orria erakusten du (backintime-config)" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Ireki Back In Time-ren webgunea nabigatzailean" + +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Aldaketen egunkaria" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" +"Ireki aldaketen erregistroa (tokikoa eskuragarri badago, bestela webekoa)" + +#: qt/app.py:573 msgid "FAQ" msgstr "FAQ" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Ireki ohiko galderak (FAQ) nabigatzailean" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Zerbait galdetu" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Akats baten berri eman" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "Honi buruz" +#: qt/app.py:584 +msgid "Translation" +msgstr "Itzulpena" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Gora" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Itzulpenean parte hartzeari buruzko mezua erakusten du berriro." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Erakutsi ezkututko fitxategiak" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Enkriptazio-trantsizioa (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "EncFS kentzeari buruzko mezua erakusten du berriro." + +#: qt/app.py:599 +msgid "About" +msgstr "Honi buruz" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Berreskuratu" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." msgstr "" -"Leheneratu hautatutako fitxategiak eta karpetak jatorrizko kokalekura." +"Leheneratu hautatutako fitxategiak edo karpetak jatorrizko kokalekura." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Berreskuratu hona" +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Leheneratu hona …" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "Leheneratu hautatutako fitxategiak eta karpetak kokaleku berrira." - -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." msgstr "" -"Leheneratu erakutsitako karpeta eta bere eduki guztia jatorrizko kokalekura." +"Leheneratu hautatutako fitxategiak edo karpetak kokaleku berri batera." -#: ../../qt/app.py:239 +#: qt/app.py:615 msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" -"Leheneratu erakutsitako karpeta eta bere eduki guztia kokaleku berrira." +"Leheneratu begi-bistako karpeta eta haren eduki guztia jatorrizko " +"kokalekura." -#: ../../qt/app.py:249 -#, python-format +#: qt/app.py:621 msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" -"Leheneratu hautatutako fitxategi edo karpetak.\n" -"Botoi hau grisa badago gehienetan da \"%(now)s\" hautatua dagoelako " -"ezkerreko babeskopien zerrendan." +"Leheneratu begi-bistako direktorioa eta bere eduki guztia kokaleku berri " +"batera." -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Babeskopiak" +#: qt/app.py:624 +msgid "Up" +msgstr "Gora" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Babeskopia" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Erakutsi ezkututko fitxategiak" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Ikusi" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Konparatu babeskopiak…" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Lasterbideak" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Askatzeko bertsio hautagaia" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" -"Karpeta hau ez dago\n" -"une honetan hautatutako babeskopian!" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Bertsio hautagai honi buruzko mezua erakusten du berriro." -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "Gehitu sartu beharrekoetan" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "Gehitu kanpoan utzi beharrekoetan" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Babeskopia" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Leheneratu" + +#: qt/app.py:723 +msgid "&Help" +msgstr "L&aguntza" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Sistemaren azpileko ikonoa" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automatikoa" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Ikono argia" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Ikono iluna" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Ikonoak bakarrik" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Testua bakarrik" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Testua ikonoen azpian" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Testua ikonoen ondoan" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" -"%(appName)s ez dago konfiguratua. Nahi duzu leheneratu aurreko " -"konfiguraziora?" +"Direktorio hau ez da existitzen\n" +"une honetan hautatutako babeskopian." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Ezin da aurkitu babeskopien karpeta.\n" -"Unitate eramangarri batean badago konekta ezazu eta sakatu Ados" +"Direktorio hau ez da existitzen\n" +"une honetan hautatutako babeskopian." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" "Leiho hau ixten baduzu Back In Time-k ezin izango du zure sistema itzali " -"babeskopia bukatzean.\n" -"Seguru zaude itxi nahi duzula?" +"babeskopia bukatzean." + +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Leihoa itxi edonola?" + +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Egina, ez da babeskopia egin behar" -#: ../../qt/app.py:667 ../../qt/app.py:719 +#: qt/app.py:1260 msgid "Working:" msgstr "Lanean:" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Egina, ez da babeskopia egin behar." +#: qt/app.py:1267 +msgid "Working" +msgstr "Lanean" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Errorea:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Errorea" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" msgstr "Bidalita:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "Abiadura:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" msgstr "ETA:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Orokorra" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Root" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Babeskopia:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Hasiera" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Berreskuratu {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Babeskopia-karpetak" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Leheneratu {path} hona…" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Seguru zaude babeskopia ezabatu nahi duzula?\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Kaixo\n" +"Dagoeneko hainbat aldiz erabili duzu Back In Time aplikazioa {language} hizkuntzan.\n" +"Instalatuta duzun Back In Time-ren bertsioaren {language}-zko itzulpenaren {perc} eginda dago. Itzulpena egiten lagundu zenezake eta modu horretan Back In Time lagunduko duzu, jakintza teknikorik izan gabe.\n" +"Bisitatu {translation_platform_url} ekarpenak egin nahi badituzu. Laguntza edo galderak badituzu, bisitatu {back_in_time_project_website}.\n" +"Barkatu eragozpenak, mezu hau ez da berriro agertuko. Mezu hau berriz ikusi dezakezu laguntza menua erabiliz.\n" +"Zure Back In Time taldea" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "Itzulpenetarako plataforma" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Webgunea" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" -"Egin fitxategi lokalen babeskopia\n" -"bukaerako '%(suffix)s'-en bidez gainidatzi edo ezabatu aurretik." +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Zure Itzulpena" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Fedibertsoko Mastodonen: {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Posta mezua bidali {link_and_label}-ri." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Posta-zerrenda {link_and_label}." -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} proiektuaren webgunean." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Arazo baten berri eman" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Bestela, nahi duzun kanala erabil dezakezu." + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Back In Time-ren bertsio hau hautagaia da eta hurrengo bertsio ofizialerako prestatzeko egonkortasun-probak egiteko da.\n" +"Ez da erabiltzailearen daturik edo telemetriarik biltzen. Hala ere, Back In Time taldeari asko interesatzen zaio Release Candidate erabiltzen ari ote den eta ea merezi duen jakitea aurreko bertsioak ematen jarraitzea.\n" +"Hori dela eta, taldeak iritzi labur bat eskatzen dizu bertsio hau probatu duzun ala ez jakiteko, nahiz eta arazorik ez aurkitu. Minutu gutxiko proba azkar batek ere asko lagunduko liguke.\n" +"Harremanetarako aukera hauek daude eskuragarri:\n" +"{contact_list}\n" +"Bertsio honetan, mezu hau ez da berriro erakutsiko, baina edonoiz atzi dezakezu laguntza-menuaren bidez.\n" +"Eskerrik asko zure laguntzagatik eta Back In Time hobetzen laguntzeagatik!\n" +"Zure Back In Time taldea" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -"Berriagoak diren fitxategiak berrizendatuko dira '%(suffix)s' kokapenarekin " -"leheneratu baino lehen.\n" -"Ez badituzu gehiago behar ezabatu ditzakezu '%(cmd)s'-rekin." +"Helmugan libre dagoen {free}-ko lekua soilik, konfiguratutako {threshold}-ko" +" atalasearen azpitik dagoena." -#: ../../qt/app.py:1038 +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Ekin babeskopia egiteari?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "{path}-eko fitxategi guztiak ezabatuko dira. Ekin?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "Jatorrizko direktorioko fitxategi berri guztiak ezabatuko dira. Ekin?" + +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" -"Leheneratu soilik falta diren fitxategiak edo\n" -"helburuko tokikoak baino berriagoak direnak.\n" -"\"rsync --update\" aukera erabiltzen." +"{BOLD}Kontuz{BOLDEND}: fitxategi-sistemaren erroko fitxategiak ezabatzeak " +"sistema osoa hondatu dezake." + +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Babeskopiaren izena" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "Ezabatu jatorrizko karpetako fitxategirik berrienak" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Ezabatu babeskopia hau?" +msgstr[1] "Ezabatu babeskopia hauek?" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" -"Leheneratu hautatutako fitxategiak edo karpetak jatorrizko kokalekura eta\n" -"ezabatu babeskopian ez dauden fitxategi/karpetak.\n" -"Honek babeskopia egitean baztertutako fitxategi/karpetak ezabatuko ditu!\n" -"Argi ibili!!!" +"Hizkuntza-ezarpenek Back In Time berrabiarazi ondoren bakarrik izango dute " +"eragina." -#: ../../qt/app.py:1083 -#, python-format +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Bertsiodun babeskopiak (root)" + +#: qt/backintime-qt-root.desktop:23 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -"Seguru zaude fitxategi hau(ek) leheneratu \n" -"nahi dituzula '%(path)s' karpetan:" +"Diskoaren erabilera murrizten duten bertsiodun babeskopietarako interfaze " +"erabilerraza (root modua)" + +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Bertsiodun babeskopiak" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "Ziur zaude fitxategi hori(ek) berreskuratu nahi duzula:" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" +msgstr "" +"Diskoaren erabilera murrizten duten bertsiodun babeskopietarako interfaze " +"erabilerraza" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "Seguru zaude '%(path)s' tokiko fitxategi berri guztiak?" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Galdera" -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" -"Seguru zaude ezabatu nahi duzula jatorrizko karpetatik berriagoak diren " -"fitxategi guztiak?" +"Egin babeskopia '{suffix}' atzizkiarekin elementu lokalak gainidatzi edo " +"ezabatu aurretik." -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" -"KONTUZ: fitxategi-sistemaren erroko fitxategiak ezabatzeak sistema osoa " -"hondatu dezake!!!" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Ikusi diskoaren edukia" +"Leheneratu baino lehen, fitxategien bertsio berriak berrizendatuko dira " +"'{suffix}' gehituta. Ez badituzu gehiago behar ezabatu ditzakezu honako " +"aginduarekin:" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Babeskopia: %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Ikusi %s -ean egindako babeskopia" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Leheneratu soilik falta diren fitxategiak edo helburuko tokikoak baino " +"berriagoak direnak, \"{rsync_example}\" aukera erabiliz." -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Berreskuratu '%s'" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Ezabatu jatorrizko karpetako fitxategirik berrienak." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Berreskuratu '%s' hona..." +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Leheneratu hautatutako fitxategiak edo direktorioak jatorrizko kokalekura " +"eta ezabatu babeskopian ez dauden fitxategi edo direktorioak. Kontu handiz " +"egin, honek babeskopia egitean baztertutako fitxategi eta karpetak ezabatuko" +" ditu." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"Seguru zaude elementu hau direktorio berrian leheneratu nahi duzula?" +msgstr[1] "" +"Seguru zaude fitxategi hauek direktorio berrian leheneratu nahi dituzula?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Ziur zaude fitxategi hau berreskuratu nahi duzula?" +msgstr[1] "Ziur zaude fitxategi horiek berreskuratu nahi dituzula?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Erabiltzaile-deia itzultzea: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" +"Erabiltzailearen deia itzultzeko script-ak shebang bat (#!) izan behar du " +"lehen lerroan (adib. {example})." -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Egileak" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Erakutsi/ezkutatu ezkutuko fitxategiak eta direktorioak (Ctrl+H)" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Itzulpenak" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Gehitu sartu beharrekoetan" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Lizentzia" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Gehitu kanpoan utzi beharrekoetan" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Hautatu erabili ohi diren elementuetatik salbuetsitakoen zerrendara " +"gehitzeko." +msgstr[1] "" +"Hautatu erabili ohi diren elementuetatik salbuetsitakoen zerrendara " +"gehitzeko." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Hautatu erabili ohi diren elementuetatik salbuetsitakoen zerrendara " +"gehitzeko." +msgstr[1] "" +"Hautatu erabili ohi diren elementuetatik salbuetsitakoen zerrendara " +"gehitzeko." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Konfiguratu hizkuntza" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Itzulia: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Sistemak lehenetsia" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Erabili sistema eragilearen hizkuntza." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Babeskopiaren erregistroaren ikuspegia" + +#: qt/logviewdialog.py:63 msgid "Last Log View" msgstr "Azken erregistroaren ikuspegia" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "Babeskopiaren erregistro ikuspegia" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Profila:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Babeskopiak:" + +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Iragazi:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Errorea, [I] Informazioa, [C] Aldaketa" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "dekodetu bide-izenak" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Guztiak" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "Akatsak" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 msgid "Changes" msgstr "Aldaketak" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informazioa" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Akatsak" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Error, [I] Information, [C] Change" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informazioa" +msgstr[1] "Informazioak" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "dekodetu bide-izenak" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync transferentzia akatsak (esperimentala)" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "Kopiatu" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Kudeatu profilak" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "Dekodetu" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Editatu" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "Nahi duzu hau kanpoan utzi?" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Gehitu" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Errorea" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Kendu" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "Galdera" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Orokorra" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "Hasi BackinTime" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Sartu" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Lanean..." +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Kanpoan utzi" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Gaur" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Ezabatu atxikipena" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Atzo" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Aukerak" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Aste honetan" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "A&ukera aurreratuak" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Joan den astean" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Leheneratu konfigurazioa" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "Hau EZ da babeskopia bat zure fitxategi lokalen ikuspegia baizik" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Profil berria" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "Azken kontrola %s" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Berrizendatu profila" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "Erakutsi erregistro osoa" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Seguru zaude \"{name}\" profila ezabatu nahi duzula?" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Editatu" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Kopiatu esteka sinbolikoak fitxategi gisa" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "Aldatu Full System Backup-entzat" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." +msgstr "" +"Kopiatu esteka sinbolikoak benetako fitxategi edo direktorio gisa " +"babeskopian. Hautatu esteka guztiak kopiatzea edo soilik iturburutik kanpora" +" seinalatzen dutenak." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "Gehitu" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Aukera honek babeskopiaren tamaina handitu dezake." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "Ezabatu" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Lehenezpenez desgaitua." + +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" +"Esteka sinboliko guztiak seinalatzen dituzten fitxategi edo direktorio " +"errealekin ordezkatzen dira. Honek babeskopien tamaina handitzen du eta " +"fitxategi berberak hainbat aldiz gorde ditzake." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Orokorra" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "'rsync --copy-links' erabiltzen du." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Modua:" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Bakarrik kanpokoak" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -"Kontuz: %(app)s-k EncFS erabiltzen du kodetzeko. Berriki egindako " -"segurtasun auditoria batek hari egindako hainbat erasoen berri eman du. Eman " -"begirada bat Backintime-ren man-eko 'A NOTE ON SECURITY' oharrari." +"Babeskopia-iturburutik kanpo seinalatzen duten estekak soilik kopiatzen dira" +" fitxategi gisa. Honek babeskopien tamaina handitzen du eta fitxategi " +"berberak hainbat aldiz gorde ditzake." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Non gorde babeskopiak" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "'rsync --copy-unsafe-links' erabiltzen du." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "Karpeta" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Edizio eta bulegoko behin-behineko fitxategiak" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "SSH ezarpenak" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacs babeskopia fitxategiak" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Ostalaria" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs automatikoki gordetako fitxategiak" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Ataka:" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Vim swap fitxategiak" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Erabiltzailea:" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Office behin-behineko fitxategiak" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Bide-izena:" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice eta beste OpenDocument Editoreen blokeo fitxategiak" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "Zifratua:" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniaturak eta behin-behineko irudiak" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Gako pribatua:" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "GNU/Linux eta bestelako unix sistemen miniaturen cachea" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "Gako fitxategia" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Windowseko miniaturen datu-basea" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "Sortu pasahitzik gabeko SSH gako berri bat." +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "MacOSeko metadatuen direktorioa" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Pasahitza" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Aplikazioen berariazko blokeoak" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Gorde pasahitza" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discord aplikazioaren lock fitxategia" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord saioaren lock fitxategia" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Mozilla Firefox eta Thunderbird lock fitxategia" + +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Cacheak eta behin behineko direktorioak" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Erabiltzailearen aplikazioen cachea" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Sistemaren behin behineko direktorioa" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Debian (eta deribatuen) GNU/Linux banaketarako paketeen cachea" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Flatpak aplikazio eta runtime biltegia" + +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Sistemaren exekuzio-direktorioak" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Kernelaren eta prozesuen informazioa" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Gailu eta bestelako hardware informazioa (sysfs interfazea)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Gailu nodoak" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Sistemaren exekuzio fitxategiak" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Beste batzuk ez iraunkorrak" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Une honetan muntatutako fitxategi-sistemen zerrenda" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Sistemaren swap fitxategia (memoria birtuala)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "GNOME fitxategi sistema birtualaren muntatze-puntua" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Berreskuratutako fitxategi-sistemako objektuak" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Askotarikoak" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Microsoft Windowsko metadatuen direktorioa" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Erabiltzailearen birziklatzeko zakarrontzia" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Sistemaren babeskopia-fitxategiak" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Salbuesteko iradokiak" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -"Cron-entzako cache-pasahitza (Segurtasun akatsa: root-ek pasahitza irakur " -"dezake)" +"Hautatu erabili ohi diren elementuak babeskopia-salbuespenetan gehitzeko." -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Aurreratua" +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Lehenetsia" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "Babeskopiaren bide osoa: " +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Berrezarri aurrez definitutako hautapenera" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Programaketa" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Eguna:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "Asteguna" +msgstr "Asteguna:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" msgstr "Ordua:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "Orduak:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "orduaren ondoren" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minutuak:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Exekutatu Back In Time unitatea konektatu bezain pronto (X egunean behin " +"bakarrik). Zure sudo pasahitza eskatuko zaizu." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" -"Erabili Back In Time behin eta berriz. Egin bereziki ordenagailua ez badabil " -"ongi." +"Erabili Back In Time behin eta berriz. Egin bereziki ordenagailua ez badabil" +" ongi." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "Guztiak:" +msgstr "Bakoitza:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Gaitu arazketa-mezuen erregistroa" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" +"Arazketa-mailako mezuak idazten ditu sistemaren erregistroan \"--debug\" " +"bidez." + +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -"Exekutatu Back In Time unitatea konektatu bezain pronto (X egunean behin " -"bakarrik).\n" -"Zure sudo pasahitza eskatuko zaizu" - -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Sartu" - -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "sartu fitxategiak eta karpetak" - -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Gehitu fitxategia" - -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Gehitu karpeta" - -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Kanpoan utzi" - -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" -"Kontuz: Komodin karaktereak ('foo*', '[fF]oo', 'fo?') ez dira " -"ezagutuko 'SSH encrypted' moduan.\n" -"Bakarrik erabil daitezke banatutako asteriskoak ('foo/*', 'foo/**/bar')" - -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Baztertu txantiloi, fitxategi edo karpeta hauek" - -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Oso gomendagarria" - -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "Gehitu lehenetsia" - -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "Utzi kanpoan hau baino handiagoak diren fitxategiak: " - -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" -"Utzi kanpoan hau baino handiagoak diren fitxategiak %(prefix)s\n" -"'rsync osoko modua' desgaituta badago horrek fitxategi berriei soilik " -"eragingo die\n" -"zeren eta rsync-entzat hori transferentzia aukera bat da, ez baztertze " -"aukera bat.\n" -"Beraz babeskopia lehendik egina zituzten fitxategi handiak mantenduko dira " -"irudian\n" -"nahiz eta aldaketak jasan." - -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Ezabaketa automatikoa" - -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Hau baino zaharragoa:" - -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Toki librea hau baino txikagoa bada:" - -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "Baldin eta nodo libreak hauek baino gutxiago badira:" - -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "Exekutatu atzeko planoan urrutiko ostalarian." - -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "Mantendu babeskopia guztiak" +"Kontuz: diagnostikoetarako soilik erabili aldi baterako, irteera kopuru " +"handia sortzen baitu." -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "egunez" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Ezgaituta" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "Mantendu eguneko babeskopia bana azkenerako" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Abiatze/berrabiaratze guztietan" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "Mantendu asteko babeskopia bana azkenerako" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "minutuero" +msgstr[1] "{n} minutuan behin" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Orduro" +msgstr[1] "{n} orduro" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "astetarako" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Ordu pertsonalizatuak" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "Mantendu hilabeteko babeskopia bana azkenerako" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Egunero" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "hilabetez" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Behin eta berriz (anacron)" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "Mantendu babeskopia bana urteko urte guztietan" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Unitatea konektatzean (udev)" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Ez ezabatu izena duten babeskopiak" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Astero" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Aukerak" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Hilero" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Gaitu notifikazioak" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Urtero" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Ezgaitu babeskopiak egitea bateriaz funtzionatzean" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Ordu" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Energia egoera ez dago eskurtagarri sistemarentzat" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Egun(ak)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "aste" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "Egin babeskopia bat aldiko." +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Hilabete" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" -"Beste babeskopiak blokeatuko dira oraingo babeskopia egiten den bitartean.\n" -"Hau aukera globala da, beraz, erabiltzaile honen profil guztiei eragiten " -"die.\n" -"Baina beste erabiltzaileentzat aktibatu behar da ere." +"Ordu pertsonalizazuak komaz banatutako ordu-zerrenda izan daiteke bakarrik " +"(adib. 8,12,18,23) edo */3 hiru orduz behin babeskopia egiteko." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "Egin berreskurapenean ordeztutako fitxategien babeskopia" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:65 +#, fuzzy +msgid "Choose an existing private key file from somewhere else." +msgstr "" +"یک فایل کلید خصوصی موجود را انتخاب کنید (معمولاً با نام **\"id_ed25519\"** و" +" در تنظیمات قدیمی‌تر **\"id_rsa\"**)." + +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:72 +#, fuzzy +msgid "Create a new SSH key without passphrase." +msgstr "ایجاد کلید SSHجدید در {path} ناموفق بود." + +#: qt/manageprofiles/sshkeyselector.py:92 +#, fuzzy, python-brace-format +msgid "Full path: {path}" +msgstr "مسیر کامل پشتیبان:" + +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "کلید خصوصی:" + +#: qt/manageprofiles/sshkeyselector.py:210 +#, fuzzy +msgid "Use system SSH configuration" +msgstr "وارد کردن پیکربندی" + +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" + +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "پروکسی SSH" + +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "میزبان(هاست):" + +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "پورت:" + +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "کاربر:" + +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." +msgstr "" +"اتصال به میزبان هدف از طریق این پروکسی (که به عنوان Jump Host نیز شناخته " +"می‌شود). برای جزئیات بیشتر، به گزینه \"-J\" در مستندات دستور ssh یا " +"\"ProxyJump\" در صفحه man ssh_config مراجعه کنید." + +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}اطلاعات{ENDBOLD}: در حالت 'SSH encrypted' فقط ستاره‌های تکی یا دوتایی " +"قابل استفاده هستند (مثلاً {example2}). سایر انواع الگوها و کاراکترهای " +"جایگزین نادیده گرفته خواهند شد (مثلاً {example1}). در این حالت، به دلیل " +"رمزگذاری توسط EncFS، نام پرونده‌ها غیرقابل پیش‌بینی خواهند بود." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "الگوها، پرونده‌ها یا پوشه‌ها را استثنا کن" + +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "استثنا کردن الگو" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "اضافه کردن فایل" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "اضافه کردن پوشه" + +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "سوال" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "استثنا کردن پرونده‌های بزرگتر از:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "استثنا کردن پرونده‌ها با اندازه بزرگتر از {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"با غیرفعال بودن «حالت کامل rsync»، این تنظیم تنها بر روی پرونده‌های جدیدی که" +" ایجاد میشود تأثیر می‌گذارد، زیرا rsync با آن به عنوان گزینه انتقال در نظر " +"می‌گیرد نه یک قاعده برای استثنا. در نتیجه، پرونده‌های بزرگ که قبلا " +"پشتیبان‌گیری شده‌اند، حتی اگر تغییر کرده باشند، در پشتیبان‌ها باقی خواهند " +"ماند." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "استثنا کردن الگو" + +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "استثنا کردن الگو" + +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "استثنا کردن فایل" + +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "استثنا کردن پوشه" + +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "غیرفعال شده زیرا این الگو در حالت 'SSH encrypted' قابل استفاده نیست." + +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"این گزینه‌ها برای تنظیمات پیشرفته هستند. فقط در صورتی آن‌ها را تغییر دهید که" +" به طور کامل از تأثیراتشان آگاه باشید." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "'rsunc' را با '{cmd}' اجرا کن:" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" +msgstr "به عنوان کار کرون" + +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" +msgstr "برروی هاست ریموت" + +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "وقتی درحال پشتیبان‌گیری دستی است" + +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "برای فعال‌سایزی این گزینه لطفا 'nocache' را نصب کنید." + +#: qt/manageprofiles/tab_expert_options.py:116 +msgid "on local machine" +msgstr "برروی ماشین محلی" + +#: qt/manageprofiles/tab_expert_options.py:130 +msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "stdout را به /dev/null در کار‌های کرون منتقل کنید." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" +"اگر یک MTA نصب شده باشد، Cron به‌صورت خودکار ایمیلی حاوی خروجی وظایف " +"زمان‌بندی‌شده ارسال خواهد کرد." + +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." +msgstr "stderr را به /dev/null در کار‌های کرون منتقل کنید." + +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" +"اگر یک MTA نصب شده باشد، Cron به‌صورت خودکار ایمیلی حاوی خروجی خطاهای وظایف " +"زمان‌بندی‌شده ارسال خواهد کرد." + +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/ثانیه" + +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "استفاده rsync از پهنای باند را محدود کن:" + +#: qt/manageprofiles/tab_expert_options.py:204 +msgid "Preserve ACL" +msgstr "حفظ ACL" + +#: qt/manageprofiles/tab_expert_options.py:222 +msgid "Preserve extended attributes (xattr)" +msgstr "حفظ ویژگی‌های گسترده (xattr)" + +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "محدود کردن به یک پرونده سامانه" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "گزینه‌ها باید بین نویسه‌های نقل‌قول قرار بگیرند مثل {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "پیست گزینه های اضافه به rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "پیشوندی که قبل از اجرای هر دستور در میزبان راه دور اجرا می‌شود." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format +msgid "" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." +msgstr "" +"متغیرها باید به‌صورت \\$FOO فرار داده شوند. این مقدار بر rsync تأثیر " +"نمی‌گذارد، بنابراین برای افزودن یک پیشوند به rsync از \"{example_value}\" " +"همراه با {rsync_options_value} استفاده کنید." + +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "پیشفرض" + +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "اضافه کردن پیشوند به دستورات SSH" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "بررسی کن اگر هاست ریموت آنلاین هست" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." +msgstr "" +"هشدار: اگر این گزینه غیرفعال باشد و میزبان راه دور در دسترس نباشد، ممکن است " +"باعث بروز خطاهای غیرمنتظره شود." + +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "" +"بررسی کن که آیا میزبان راه‌دور از تمامی دستورات ضروری پشتیبانی می‌کند یا " +"خیر." + +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." +msgstr "" +"هشدار: اگر این گزینه غیرفعال باشد و میزبان راه دور از تمام دستورات موردنیاز " +"پشتیبانی نکند، ممکن است باعث بروز خطاهای غیرمنتظره شود." + +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(پیشفرض: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "غیرفعال" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "فعال" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "حالت:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "پیشتیبان‌ها کجا ذخیره شوند" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "تنظیمات SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "مسیر:" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "نمایه جدید" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "رمز عبور" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "رمز عبور را در دسته کلید ذخیره کنید" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "رمز عبور کش برای کرون (مشکل امنیتی: ریشه می‌تواند رمز عبور را بخواند)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "پیشرفته" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "مسیر کامل پشتیبان:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "کلمه‌عبور مطابقت ندارد." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." +msgstr "" + +#: qt/manageprofiles/tab_general.py:560 +#, fuzzy +msgid "Proceed with copying the SSH key?" +msgstr "با پشتیبان گیری ادامه می دهید؟" + +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." +msgstr "" + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "اصالت میزبان {host} قابل تأیید نیست." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "اثر انگشت کلید {keytype} عبارت است از:" + +#: qt/manageprofiles/tab_general.py:613 +#, fuzzy +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "" +"لطفا این اثر انگشت را تأیید کنید. آیا به پرونده 'known_hosts' اضافه شود؟" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "آیا واقعا میخواهید پوشه پشتیبان‌گیری را تغییر دهید؟" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "" + +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "کلید خصوصی SSH" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." +msgstr "" + +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" + +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "ایجاد کلید SSHجدید در {path} ناموفق بود." + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "پرونده‌ها و پوشه‌ها را شامل کن" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." +msgstr "" +"\"{path}\" یک لینک نمادین است. از مقصد پیوند شده تا زمانی که شامل آن نگردد " +"پشتیبان‌گیری انجام نخواهد شد." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "آیا در عوض هدف لینک نمادین را شامل آن می‌کنید؟" + +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "شامل کردن فایل" + +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "شامل کردن پوشه" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "فعال کردن اعلان‌ها" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "غیرفعال کردن پشتیبان‌گیری‌ها زمان استفاده از باتری" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "وضعیت باتری از طرف سیستم در دسترس نیست" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "اجرای فقط یک پشتیبان‌گیری در زمان" + +#: qt/manageprofiles/tab_options.py:56 +msgid "" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." +msgstr "" +"سایر پشتیبان‌گیری‌ها تا زمانی که پشتیبان‌گیری فعلی کامل نشده مسدود خواهند " +"شد. این یک تنظیم سراسری است، به این معنا که بر تمام نمایه‌های این کاربر " +"تأثیر خواهد گذاشت. با این حال، باید برای سایر کاربران نیز فعال شود." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "پشتیبان‌گیری از فایل‌های تغییر مکان یافته در هنگام بازگردانی" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "ادامه در صورت بروز خطا (نگه‌داشتن پشتیبان‌های کامل نشده)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "از سرجمع برای شناسایی تغییرات استفاده کن" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "چه تغییرات صورت گرفته باشد یا نه، یک پشتیبان جدید ایجاد کن." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." +msgstr "" + +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." +msgstr "" + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "سطح لاگ:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "هیچکدام" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "راهنمای کاربر" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, fuzzy, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "" +"قوانین زیر از بالا به پایین پردازش می‌شوند. قوانین بعدی بر قوانین قبلی " +"اولویت دارند. برای جزئیات و نمونه‌ها به {manual} مراجعه کنید." + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "باز کردن راهنمای کاربر در مرورگر." + +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "جدیدترین پشتیبان را نگه دار." + +#: qt/manageprofiles/tab_remove_retention.py:241 +#, fuzzy +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "آخرین یا جدیدترین پشتیبان تحت هر شرایطی حفظ می‌شود." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "رفتار نمی تواند تغییر کند." + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "پشتیبان‌های نام‌گذاری‌شده را نگه دار." + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." +msgstr "" +"پشتیبان‌هایی که علاوه بر زمان‌سنجی معمول، نام‌گذاری شده‌اند، تحت هر شرایطی " +"حفظ خواهند شد و حذف نخواهند شد." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "سال(ها)" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "حذف پشتیبان‌های قدیمی‌تر از" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "روزهای کامل. روز جاری نادیده گرفته می‌شود." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" +"هفته‌های تقویم با دوشنبه به عنوان اولین روز. هفته جاری نادیده گرفته می‌شود." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "دوره‌های ۱۲ ماهه. ماه جاری نادیده گرفته می‌شود." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "سیاست‌های نگهداری" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "در پس‌زمینه برروی میزبان راه‌دور اجرا کن." + +#: qt/manageprofiles/tab_remove_retention.py:313 +#, fuzzy +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"فرآیند حذف هوشمند مستقیما روی ماشین راه دور اجرا می‌شود، نه به‌صورت محلی. " +"دستورات \"bash\"، \"screen\" و \"flock\" باید روی ماشین راه دور نصب و در " +"دسترس باشند." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "" +"اگر این گزینه انتخاب شود، Back In Time ابتدا ماشین راه دور را آزمایش خواهد " +"کرد." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "روزها از امروز شمرده می‌شوند." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "تمام پشتیبان‌ها را برای همیشه نگه دار" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "روز‌(ها)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "آخرین پشتیبان هر روز را برای همیشه نگه دار" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "" +"هفته‌ها از هفته جاری که در حال اجرا است شروع می‌شوند. هفته از روز دوشنبه " +"آغاز می‌شود." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "آخرین پشتیبان هر هفته را برای همیشه نگه دار" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "هفته(ها)." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "ماه‌ها به‌عنوان ماه‌های تقویمی از ماه جاری شمرده می‌شوند." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "آخرین پشتیبان هر ماه را برای همیشه نگه دار" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "ماه(ها)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "سال‌ها به‌عنوان سال‌های تقویمی از سال جاری شمرده می‌شوند." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "آخرین پشتیبان هر سال را نگه دار برای" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "تمام سال ها." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "... فضای آزاد کمتر باشد از" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "... در صورتی که تعداد inode آزاد کمتر باشد از" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "قدیمی‌ترین پشتیبان را حذف کن اگر …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "" + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "میانبرها" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "پوشه‌های پشتیبان" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "نمایه: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "نمایه: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "آخرین لاگ را ببین" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "اجرای {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "درحال کار…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "وارد کردن پیکربندی" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "وارد کردن" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "درحال کار…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" +"پوشه پشتیبان را که پرونده پیکربندی باید از آن وارد شود را انتخاب کنید. مسیر " +"ممکن است به شکل زیر باشد: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" +"اگر دایرکتوری بر روی یک درایو خارجی یا راه دور قرار دارد، باید پیش از آن " +"به‌صورت دستی سوار شود." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "" + +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "نمایش فایل های مخفی" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "پرونده‌ها و پوشه‌ها را شامل کن" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "ایجاد پرونده در این پوشه ناموفق بود:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "" + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "لاگ را کامل نشان بده" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "شمارش معکوس برای خاموشی" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "پشتیبان‌گیری به پایان رسید." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "لغو خاموشی" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "خاموشی فوری" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "سامانه پس از {n} ثانیه خاموش خواهد شد." +msgstr[1] "سامانه پس از {n} ثانیه دیگر خاموش خواهد شد." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "گزینه‌ها درباره مقایسه پشتیبان‌ها" + +#: qt/snapshotsdialog.py:68 +msgid "Command:" +msgstr "دستور:" + +#: qt/snapshotsdialog.py:72 +msgid "Parameters:" +msgstr "پارامتر ها:" + +#: qt/snapshotsdialog.py:77 +msgid "Use %1 and %2 for path parameters" +msgstr "از ۱٪ و ۲٪ برای پارامترهای مسیر استفاده کن" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "لطفا دستور diff را تنظیم کنید یا روی لغو کلیک کنید." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"دستور \"{cmd}\" در این سامانه یافت نشد. لطفا دستور دیگری امتحان کنید یا روی " +"لغو کلیک کنید." + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" +"هیچ پارامتری برای دستور diff تنظیم نشده است. از مقدار پیش‌فرض \"{params}\" " +"استفاده می‌شود." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "پشتیبان‌ها" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "فقط پشتیبان‌های متفاوت" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "فقط پشتیبان‌هایی را لیست کن که برابر هستند با:" + +#: qt/snapshotsdialog.py:171 +msgid "Deep check (more accurate, but slow)" +msgstr "بررسی ژرفناک (بیشتر دقیق، ولی کند)" + +#: qt/snapshotsdialog.py:192 +msgid "Delete" +msgstr "پاک کردن" + +#: qt/snapshotsdialog.py:197 +msgid "Select All" +msgstr "انتخاب همه" + +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "مقایسه" + +#: qt/snapshotsdialog.py:232 +msgid "Go To" +msgstr "برو به" + +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "گزینه‌ها" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "مقایسه یک پشتیبان با خودش ممکن نیست، زیرا یک مقایسه تکراری خواهد بود." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "آیا واقعا می‌خواهید {file_or_dir} را در پشتیبان {backup_id} حذف کنید؟" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "آیا واقعا می‌خواهید {file_or_dir} را در {count} پشتیبان حذف کنید؟" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "هشدار: این عمل قابل بازگشت نیست." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "آیا می‌خواهید {path} را از پشتیبان‌گیری‌های آینده مستثنی کنید؟" + +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "ریشه" + +#: qt/statusbar.py:87 +msgid "" +"Back In Time is currently running with root privileges (full system access)" +msgstr "" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "امروز" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "دیروز" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "این هفته" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "آخرین هفته" + +#: qt/timeline.py:88 +msgid "This month" +msgstr "" + +#: qt/timeline.py:95 +msgid "Last month" +msgstr "" + +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "آخرین بررسی {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "این یک پشتیبان نیست بلکه نمای زنده از پرونده‌های محلی است." diff --git a/common/po/fi.po b/common/po/fi.po index a4c6eeed1..d59a66bed 100644 --- a/common/po/fi.po +++ b/common/po/fi.po @@ -1,1658 +1,2589 @@ -# Finnish translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Ellen Rönnholm # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:06+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Finnish \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-01-31 12:36+0000\n" +"Last-Translator: artnay \n" +"Language-Team: Finnish \n" +"Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"Kaikki tässä projektissa käytetyt lisenssit sijaitsevat hakemistossa " +"{dir_link}. Katso {readme_link} tiedostokohtaisten lisenssi- ja " +"tekijänoikeustietojen purkamiseksi SPDX-metatietoja käyttämällä." -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Varoitus" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Profiili \"%s\" on jo olemassa !" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Pääprofiili" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Et voi poistaa viimeistä profiilia !" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Paikallinen (EncFS salattu)" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Poissa käytöstä" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS salattu)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Joka käynnistyksessä/uudelleenkäynnistyksessä" +#: common/config.py:237 +msgid "Local" +msgstr "Paikallinen" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "5 minuutin välein" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Paikallinen salattu" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "10 minuutin välein" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Salaus" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Yksityinen SSH-avain" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profiili: ”{name}”" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Varmuuskopioiden hakemisto ei ole kelvollinen." -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Vähintään yksi hakemisto on valittava varmuuskopiointia varten." -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Hakemisto: {path}" -#: ../../common/config.py:87 -msgid "Custom Hours" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Tätä hakemistoa ei voi sisällyttää varmuuskopioon, koska se on osa itse " +"varmuuskopion kohdetta." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Joka päivä" - -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:366 +#, fuzzy, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"Arvon \"Poista vanhin varmuuskopio, jos vapaata tilaa on vähemmän kuin\" " +"({val_one}) on oltava pienempi tai yhtä suuri kuin kynnysarvo \"Varoita, jos" +" vapaata levytilaa on vähemmän kuin\" ({val_two}):lle." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" +"Säädä asetuksia niin, että varmuuskopioiden poistoraja ei ole korkeampi kuin" +" varoitusraja." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Joka viikko" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Udevin ajoittaminen ei toimi tilassa {mode}" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Joka kuukausi" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Uuden crontabin kirjoittaminen epäonnistui." -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Päivä(ä)" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron ei ole käynnissä, vaikka crontab-komento on käytettävissä. Ajoitetut " +"varmuuskopiointityöt eivät käynnisty. Cron saattaa olla asennettuna, mutta " +"ei ole käytössä. Kokeile suorittaa kaksi komentoa \"systemctl enable cron\" " +"ja \"systemctl start cron\" tai ota yhteyttä käytössä olevan GNU/Linux-" +"jakelun tukikanaviin saadaksesi apua." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Määrityksen tallentaminen epäonnistui" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Määrityksen lataaminen epäonnistui" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Profiili ”{name}” on jo olemassa." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Tätä ei voi kumota." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Ei voi liittää '{command}'" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Salatun hakemiston määritystä ei löytynyt." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Luodaanko uusi salattu hakemisto?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Peru" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Viikko(a)" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Vahvista syöttämällä EncFS-salasana uudelleen." -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Vuosi(a)" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFS-salasanat eivät täsmää." -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Ota otos" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Ei voi liittää '{command}'" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Ei voi irrottaa {mountprocess} kohteesta {mountpoint}." -#: ../../common/config.py:129 -msgid "Local" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" +"Komentoa \"{command}\" ei löytynyt. Ole hyvä ja asenna se (esim. " +"\"{installcommand}\" kautta)" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Liitospiste {mntpoint} ei ole tyhjä." -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Syötä salasana {mode} profiilille \"{profile}\":" -#: ../../common/config.py:131 -msgid "Local encrypted" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" +"Profiilille {profile_id} ei voitu asentaa udev-sääntöä. DBus-palvelu " +"”{dbus_interface}” ei ole käytettävissä." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Kohteelle ”{path}” ei löytynyt UUID:tä" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "EPÄONNISTUI" -#: ../../common/config.py:135 -msgid "Default" -msgstr "" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Palauta käyttöoikeudet" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Valmis" -#: ../../common/config.py:137 -msgid "AES192-CTR" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" msgstr "" +"Seuraavilla sisällytysluettelon merkinnöillä ei ole vastaavaa tiedostoa tai " +"hakemistoa varmuuskopiointilähteessä:" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Viivästetään varmuuskopiointia akkukäytön ajan" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Varmuuskopioiden hakemistoa ei löydy." -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Jos se on irrotettavalla asemalla, kytke se." -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Odotetaan {n} sekunti." +msgstr[1] "Odotetaan {n} sekuntia." -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Varmuuskopion {snapshot_id} luominen epäonnistui." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Ole kärsivällinen; viimeistellään…" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Hakemistoa ei voi luoda." -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Tallennetaan määritystiedostoa…" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Tallennetaan käyttöoikeuksia…" -#: ../../common/config.py:147 -msgid "ARCFOUR" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." msgstr "" +"Löytyi epätäydellinen varmuuskopio {snapshot_id}, jota voidaan jatkaa." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Pääprofiili" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profiili: \"%s\"" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Otoskansio ei ole pätevä !" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "" +"Poistetaan epätäydellinen {snapshot_id}-hakemisto viime suorituskerrasta" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Valitse ainakin yksi kansio varmuuskopioitavaksi!" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Ei voi poistaa hakemistoa" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Varmuuskopiokansiota ei voi lisätä!" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Luodaan varmuuskopiota" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Varmuuskopionkansion alikansiota ei voi lisätä!" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Onnistui" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s ei ole kansio!" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Osittain siirretty johtuen virheestä" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" +"Osittain siirretty johtuen puuttuvista lähdetiedostoista ( kts. 'man rsync')" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Ei voi kirjoittaa: %s\n" -"Oletko varma että sinulla on kirjoitusoikeudet?" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' päättyi paluukoodilla {exit_code}" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Katso lisätietoja: 'man rsync'" -#: ../../common/config.py:407 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" +"Negatiiviset rsync-poistumiskoodit ovat signaalinumeroita, katso 'kill -l' " +"ja 'man kill'" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Kopioi linkit (ratkaise symboliset linkit)" - -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Lisäasetukset" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Mikään ei ole muuttunut: uusi varmuuskopio ei ole tarpeen" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Ei voi uudelleennimetä {new_path} {path}:ksi." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Crontab-ohjelmaa ei löydy.\n" -"Onko cron-ohjelmaa asennettu?\n" -"Jos ei ole, kaikki automaattiset varmuuskopiot kannattaa ottaa pois käytöstä" +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "Toteuta säännöt poistaaksesi vanhat varmuuskopiot" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "Sovella säilytyskäytäntö" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Yritetään pitää vapaa vähimmäistila" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Yritetään pitää vähintään {perc} inodeja vapaana" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Nyt" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Ei voi liittää {sshfs}" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agentia ei löytynyt. Varmista, että se on asennettu." -#: ../../common/encfstools.py:136 +#: common/sshtools.py:489 msgid "" -"\n" -"Create a new encrypted folder?" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"Yksityisen SSH-avaimen lukitusta ei voida avata. Salasana on väärin tai ei " +"käytettävissä cronia varten." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Etäsijainti on olemassa muttei kansio." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Etäsijaintiin ei voi kirjoittaa." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Etäsijainti ei ole ohjelmatiedosto." -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Etäsijaintia ei voitu luoda." -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Ota otos" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Etäkone {host} ei tue komentoa {command}" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Check-komennot palvelimella {host} antoivat tuntemattoman virheen" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Etäkone {host} ei tue kovia linkkejä" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Kopioi julkinen ssh-avain \"{pubkey}\" palvelimelle \"{host}\"." -#: ../../common/mount.py:590 -#, python-format +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Ole hyvä ja syötä käyttäjän \"{user}\" salasana." + +#: common/tools.py:382 +#, python-brace-format msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"Kohdetiedostojärjestelmä kohteelle {path} on alustettu NTFS:llä, jolla on " +"tunnettuja yhteensopimattomuuksia UNIX-tyylisten tiedostojärjestelmien " +"kanssa." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} ei ole kelvollinen hakemisto." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Seuraavan hakemiston luominen epäonnistui:" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Kirjoitusoikeutta saatetaan rajoittaa." -#: ../../common/snapshotlog.py:62 +#: common/tools.py:471 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"Kohdetiedostojärjestelmä kohteelle {path} on alustettu FATilla, joka ei tue " +"kiintolinkkejä. Käytä alkuperäistä GNU/Linux-tiedostojärjestelmää." -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "EPÄONNISTUI!" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Palauta oikeudet:" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." +msgstr "" +"Kohdetiedostojärjestelmä kohteelle {path} on SMB:n kautta liitetty jako. " +"Varmista, että SMB-etäpalvelin tukee symbolisia linkkejä tai aktivoi " +"\"{copyLinks}\" kohdassa \"{expertOptions}\"." -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Valmis" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Kopioi linkit (ratkaise symboliset linkit)" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Asiantuntija-asetukset" -#: ../../common/snapshots.py:669 +#: common/tools.py:491 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" -"Otoskansiota ei löydy.\n" -"Jos otoskansio sijaitsee ulkoisella asemalla, liitä asema tietokoneeseen." +"Kohdetiedostojärjestelmä kohteelle {path} on sshfs:n kautta liitetty jako. " +"Sshfs ei tue kiinteitä linkkejä. Käytä sen sijaan tilaa \"SSH\"." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Odotetaan %s sekunti." -msgstr[1] "Odotetaan %s sekuntia." +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Tiedoston luominen epäonnistui tässä hakemistossa:" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Otoksen %s otto epäonnistui !!!" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Tietoja - Back In Time" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Viimeistellään" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Tekijänoikeus:" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Ei voinut luoda kansiota: %s" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Tekijät:" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Tallenna asetustiedosto ..." +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Käännökset:" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Tallenna käyttöoikeudet ..." +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Ellen Rönnholm" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Käännösten tekijöiden tietoja ei ole saatavilla nykyiselle kielelle." -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "tätä linkkiä" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "Napsauta {thislink} nähdäksesi kaikkien kielten kääntäjien tiedot." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Ei voida poistaa kansiota: %s" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Projektin verkkosivusto" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Ota otos" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Käyttöopas" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Avaa käyttöopas selaimessa (paikallinen jos saatavilla, muuten verkossa)" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Versio{BOLDEND}: {version}" + +#: qt/app.py:332 +#, fuzzy, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"{app_name} näyttää käynnistettävän ensimmäistä kertaa, koska asetuksia ei " +"löytynyt." -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Älykäs poisto" +#: qt/app.py:337 +#, fuzzy +msgid "" +"Import an existing configuration from a backup location or another computer?" +msgstr "" +"Tuodaanko olemassa oleva kokoonpano (varmuuskopion kohdehakemistosta tai " +"toisesta tietokoneesta)?" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Poista vanhat otokset" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Paina sitten OK." -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Pidä vähän vapaata tilaa" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Luo varmuuskopio" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" +"Käytä muokkausajankohtaa ja kokoa tiedoston muokkauksen havaitsemiseen." -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "VIRHEITÄ!" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Luo varmuuskopio (tarkistussummatila)" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Nyt" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Havaitse muutokset tarkistussummista." -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Keskeytä varmuuskopiointi" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Jatka varmuuskopiointia" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Pysäytä varmuuskopiointi" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Päivitä varmuuskopioiden luettelo" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Nimeä varmuuskopio" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Poista varmuuskopio" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Näytä varmuuskopioinnin loki" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Näytä valittujen varmuuskopioiden loki." -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Avaa viimeisin varmuuskopion loki" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Näytä viimeisimmän varmuuskopion loki." -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Hallinnoi profiileja…" -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Muokkaa käyttäjäkutsua" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:532 +msgid "Shutdown" +msgstr "Sammuta" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Sammuta järjestelmä varmuuskopioinnin valmistuttua." -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Asennuskieli…" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Käytä tarkistussummia muutosten havaitsemiseen" +#: qt/app.py:540 +msgid "Exit" +msgstr "Lopeta" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "'man'-sivu: Back In Time" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Näyttää 'man'-sivun Back In Time (backintime) :stä" + +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "'man'-sivu: Profiilien konfigurointitiedosto" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" +"Näyttää 'man'-sivun profiilien konfigurointitiedostosta (backintime-config)" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Päivitä otoslista" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Avaa Back In Time -verkkosivusto selaimessa" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Otoksen nimi" +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "Muutosloki" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Poista otos" +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "Avaa muutosloki (paikallinen jos saatavilla, muuten verkossa)" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Selaa otoslokia" +#: qt/app.py:573 +msgid "FAQ" +msgstr "UKK" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Selaa viimeisintä lokia" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Avaa Usein kysytyt kysymykset (UKK) selaimessa" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Asetukset" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "Esitä kysymys" -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "Ilmoita ohjelmavirheestä" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:584 +#, fuzzy +msgid "Translation" +msgstr "Käännös" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "Lopeta" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Näytä viesti kääntämiseen osallistumisesta uudelleen." -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Ohje" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Encryption Transition (EncFS)" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Näyttää viestin EncFS:n poistosta uudelleen." -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Verkkosivu" +#: qt/app.py:599 +msgid "About" +msgstr "Tietoa" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" -msgstr "" +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Palauta" -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "Palauta valitut tiedostot tai hakemistot alkuperäiseen sijaintiin." -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "" +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Palauta kohteeseen…" -#: ../../qt/app.py:171 -msgid "Report a bug" +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "Palauta valitut tiedostot tai hakemistot uuteen sijaintiin." + +#: qt/app.py:615 +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Palauta tällä hetkellä näytettävä hakemisto ja kaikki sen sisältö " +"alkuperäiseen sijaintiin." -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "Tietoja ohjelmasta" +#: qt/app.py:621 +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "Palauta näytettävä hakemisto ja kaikki sen sisältö uuteen sijaintiin." -#: ../../qt/app.py:204 +#: qt/app.py:624 msgid "Up" msgstr "Ylös" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" msgstr "Näytä piilotiedostot" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Palauta" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Vertaa varmuuskopioita…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Julkaisuehdokas" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Palauta kohteeseen ..." +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Näyttää viestin tästä julkaisuehdokkaasta uudelleen." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Varmuuskopio" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Palauta" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Ohje" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Otokset" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Ilmoitusalueen kuvake" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automaattinen" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:784 +msgid "Light icon" +msgstr "Vaalea kuvake" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Sijainnit" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Tumma kuvake" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Vain kuvakkeet" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:811 +msgid "Text only" +msgstr "Vain teksti" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Teksti kuvakkeiden alla" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Teksti kuvakkeen vieressä" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Tätä hakemistoa ei ole olemassa nykyisessä\n" +"valitussa varmuuskopiossa." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Otoskansiota ei löydy.\n" -"Jos kansio on ulkoisella asemalla, kytke laite kiinni ja paina OK" +"Tätä hakemistoa ei ole olemassa nykyisessä\n" +"valitussa varmuuskopiossa." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Jos tämä ikkuna suljetaan, Back In Time ei voi sammuttaa järjestelmää " +"varmuuskopioinnin valmistuttua." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "Käsitellään:" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Suljetaanko ikkuna silti?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" msgstr "Valmis, varmuuskopiota ei tarvittu" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Virhe:" +#: qt/app.py:1260 +msgid "Working:" +msgstr "Käsitellään:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Käsitellään" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Virhe" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "Lähetetty:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "" +msgstr "Nopeus:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Yleiset" +msgstr "Arvioitu saapumisaika (ETA):" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Root" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Varmuuskopio:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Home" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Palauta {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Varmuuskopiokansiot" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Palauta {path} kohteeseen…" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Hei,\n" +"Olet käyttänyt Back In Time -sovellusta kielellä: {language} jo jonkin aikaa.\n" +"Ohjelmaversiosi käännös kielelle {language} on {perc} valmis. Riippumatta teknisestä osaamisestasi voit osallistua käännöksen ja sitä kautta myös Back In Time -ohjelman kehitykseen itsekin.\n" +"Siirry osoitteeseen {translation_platform_url} jos haluat osallistua. Lisätietoja saat myös osoitteesta: {back_in_time_project_website}.\n" +"Pyydämme anteeksi keskeytystä eikä tätä ilmoitusta näytetä uudestaan. Tämä viesti on saatavilla koska tahansa help -menun kautta.\n" +"Sinun Back In Time Team" + +#: qt/app.py:1653 +#, fuzzy +msgid "translation platform" +msgstr "Käännökset" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Sivusto" + +#: qt/app.py:1672 +#, fuzzy +msgid "Your translation" +msgstr "Käännökset" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Fediversumissa Mastodonissa: {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Sähköposti {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Postituslista {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} projektin verkkosivustolla." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Avaa vikalippu" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Vaihtoehtoisesti voit käyttää toista valitsemaasi kanavaa." + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Tämä Back In Time -versio on Julkaisuehdokas, ja se on tarkoitettu ensisijaisesti vakauden testaamiseen seuraavaa virallista julkaisua varten.\n" +"Käyttäjätietoja tai telemetriaa ei kerätä. Back In Time -joukkue on kuitenkin hyvin kiinnostunut tietämään, käytetäänkö Julkaisuehdokasta ja kannattaako tällaisten julkaisua edeltävien versioiden tarjoamista jatkaa.\n" +"Siksi joukkue pyytää ystävällisesti lyhyttä palautetta siitä, oletko testannut tätä versiota, vaikka et olisi kohdannut mitään ongelmia. Jopa muutaman minuutin mittainen nopea testiajo auttaisi meitä paljon.\n" +"Käytettävissä ovat seuraavat yhteydenottovaihtoehdot:\n" +"{contact_list}\n" +"Tässä versiossa tätä viestiä ei enää näytetä, mutta se on käytettävissä milloin tahansa ohjevalikon kautta.\n" +"Kiitos tuestasi ja siitä, että autat meitä parantamaan Back In Timea!\n" +"Back In Time -joukkueesi" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -"Oletko varma että haluat poistaa otoksen:\n" -"%s" +"Kohteessa on käytettävissä vain {free} vapaata tilaa, joka on alle " +"määritetyn kynnysarvon {threshold}." -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Jatketaanko varmuuskopiointia?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Kaikki uudemmat tiedostot polussa {path} poistetaan. Jatketaanko?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Kaikki uudemmat tiedostot alkuperäisessä hakemistossa poistetaan. " +"Jatketaanko?" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Varoitus{BOLDEND}: Tiedostojen poistaminen tiedostojärjestelmän " +"juuressa voi rikkoa koko järjestelmän." + +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Varmuuskopion nimi" + +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Poistetaanko tämä varmuuskopio?" +msgstr[1] "Poistetaanko nämä varmuuskopiot?" -#: ../../qt/app.py:1038 +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "Kielivalinta astuu voimaan uudelleenkäynnistyksen jälkeen." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Versioidut varmuuskopiot (root)" + +#: qt/backintime-qt-root.desktop:23 msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Versioidut varmuuskopiot" + +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -#: ../../qt/app.py:1072 +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Kysymys" + +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Luo varmuuskopiot, joiden lopussa on {suffix} ennen paikallisten elementtien" +" korvaamista tai poistamista." -#: ../../qt/app.py:1083 -#, python-format +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Ennen palauttamista tiedostojen uudemmat versiot nimetään uudelleen ja " +"niihin lisätään {suffix}. Nämä tiedostot voidaan poistaa seuraavalla " +"komennolla:" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Palauta vain elementtejä, joita ei ole olemassa tai jotka ovat uudempia kuin" +" kohteessa olevat. Käytetään vaihtoehtoa \"{rsync_example}\"." -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Poista uudemmat elementit alkuperäisestä hakemistosta." -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:135 msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" - -#: ../../qt/app.py:1105 +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Palauta valitut tiedostot tai hakemistot alkuperäiseen sijaintiinsa ja " +"poista tiedostot tai hakemistot, joita ei ole varmuuskopiossa. Ole erittäin " +"varovainen, koska tämä poistaa tiedostot ja hakemistot, jotka poistettiin " +"varmuuskopion luomisen aikana." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Haluatko todella palauttaa tämän elementin uuteen hakemistoon?" +msgstr[1] "Haluatko todella palauttaa nämä elementit uuteen hakemistoon?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Haluatko varmasti palauttaa tämän elementin?" +msgstr[1] "Haluatko varmasti palauttaa nämä elementit?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Käyttäjän takaisinkutsu: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"user-callback-skriptin ensimmäisellä rivillä on oltava shebang (esim. " +"{example})." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Selaa nykyistä levyn sisältöä" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Otos: %s" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Näytä/piilota piilotetut tiedostot ja hakemistot (Ctrl+H)" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Selaa otosta joka on tehty %s" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Lisää mukaan otettaviin" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Palauta '%s'" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Lisää pois jätettäviin" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Asennuskieli" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Käännetty: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Järjestelmän oletus" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Käytä käyttöjärjestelmän kieltä." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Varmuuskopiolokin näkymä" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Viimeisin lokinäkymä" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Palauta '%s' kohteeseen ..." +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "Profiili:" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Varmuuskopiot:" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" - -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" - -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "Profiili:" - -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Suodatin:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Virhe, [I] Tietoa, [C] Muutokset" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "dekoodaa polut" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Kaikki" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Muutokset" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Virheet" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Muutokset" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Tieto" +msgstr[1] "Tiedot" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Tiedot" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync-siirtoviat (kokeellinen)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Virhe, [I] Tietoa, [C] Muutokset" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Hallinnoi profiileja" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Muokkaa" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Lisää" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Poista" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Yleisasetukset" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Ota mukaan" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Jätä pois" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Poista & säilyttäminen" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Valinnat" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "A&siantuntija-asetukset" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Palauta määritys" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Uusi profiili" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Muuta profiilin nimeä" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Haluatko poistaa profiilin \"{name}\"?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Kopioi symboliset linkit tiedostoina" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Tämä valinta saattaa kasvattaa varmuuskopion kokoa." + +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Pois käytöstä oletusarvoisesti." + +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" msgstr "" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Työn alla..." +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacs-varmuuskopiotiedostot" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Tänään" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs-automaattitallennuksen tiedostot" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Eilen" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Tällä viikolla" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Officen väliaikaistiedostot" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Viime viikolla" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOfficen ja muiden OpenDocument-muokkainten lukkotiedostot" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Muokkaa" - -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discord-sovelluksen lukkotiedosto" + +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord-istunnon lukkotiedosto" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Mozilla Firefoxin ja Thunderbirdin lukkotiedosto" + +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Varmuuskopiohakemistot" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Yleiset" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "Ei voi poistaa hakemistoa" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Otosten tallennuspaikka" +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Näytä piilotetut hakemistot" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Verkkonimi:" - -#: ../../qt/settingsdialog.py:180 -msgid "Port:" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Käyttäjä:" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Sekalaiset" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Pysäytä varmuuskopiointi" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Jätä pois hakemistot" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Lisäasetukset" +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Oletusasetukset" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Aikataulu" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Päivä:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" msgstr "Viikonpäivä:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Tunti:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Aika:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" +msgstr "Tunnit:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "tunnin jälkeen" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minuutit:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" +"Suorita Back in time heti, kun asema on kytketty (vain kerran X päivässä). " +"sudo-salasanan kehote tulee näkyviin." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Suorita BackInTime toistuvasti. Tästä on apua, ellei kone ole aina " +"käynnissä." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" +msgstr "Joka:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Ota viankorjausviestien lokiin kirjaaminen käyttöön" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Kirjoittaa viankorjaustason viestejä järjestelmälokiin komennon '--debug' " +"myötä." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Huomio: Käytä tätä vain väliaikaisesti diagnostiikkaan, koska se tuottaa " +"suuren määrän ulostuloa." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Sisällytä" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Ei käytössä" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Sisällytä tiedostot ja hakemistot" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Käynnistysten yhteydessä" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Minuutin välein" +msgstr[1] "{n} minuutin välein" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Tunnin välein" +msgstr[1] "Joka {n} tunti" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Lisää tiedosto" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Mukautetut tunnit" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Lisää kansio" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Päivittäin" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Poissulje" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Toistuva (anacron)" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Asemaa kytkettäessä (udev)" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Poissulje hakuehdoilla, tiedoilla tai kansioilla" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Viikoittain" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Erityisesti suositeltu:" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Kuukausittain" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Joka vuosi" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Tunti/Tunnit" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Päivä(ä)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Viikko(a)" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Kuukausi/Kuukaudet" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Mukautettujen tuntien on oltava pilkuin erotettu tuntiluettelo (esim. " +"8,12,18,23) tai */3 varmuuskopioille kolmen tunnin välein." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Automaattinen poisto" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Eldri enn:" - -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Um tað tøka plássið er minni enn:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." msgstr "" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "SSH privatur lykil:" + +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" msgstr "" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" msgstr "" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Vertur:" + +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" msgstr "" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Brúkari:" + +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" msgstr "" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Kostir" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Legg fílu til" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Virkja kunningar" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Legg skjáttu til" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" msgstr "" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" msgstr "" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." msgstr "" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" msgstr "" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" msgstr "" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" msgstr "" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Einki" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Tak fílu við" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Broytingar & brek" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Tak skjáttur við" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" msgstr "" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" msgstr "" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" msgstr "" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" msgstr "" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." msgstr "" -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" msgstr "" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" msgstr "" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" msgstr "" -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." msgstr "" -#: ../../qt/settingsdialog.py:839 -msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" msgstr "" -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." msgstr "" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 +#: qt/manageprofiles/tab_expert_options.py:293 msgid "default" msgstr "" -#: ../../qt/settingsdialog.py:870 +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:308 msgid "Check if remote host is online" msgstr "" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" msgstr "" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" msgstr "" -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Hvar skullu støðumyndirnar goymast" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" msgstr "Nýtt umhvarv" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Nýnevn umhvarv" +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Er tú viss/ur í at strika umhvarvið \"%s\" ?" +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Framkomið" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Loyniorðið er ikki líka." + +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" msgstr "" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "" + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "" + +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Gera eina nýggja bronglaða mappu?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "" + +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "SSH privatur lykil" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." msgstr "" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" +#: qt/manageprofiles/tab_include.py:48 +#, fuzzy +msgid "Include files and directories" +msgstr "Tak skjáttur við" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" msgstr "" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" msgstr "Tak fílu við" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Tak skjáttur við" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Virkja kunningar" + +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" +msgstr "Útseta trygaravrit ímeðan streymur ikki er tilsettur" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Tak skjáttur við" +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Er tú viss/ur í at broyta støðumyndaskjáttu ?" +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" msgstr "" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." +msgstr "" + +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Einki" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Tak støðumynd burtur." + +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Tak støðumynd burtur." + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Ár" + +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Tak støðumynd burtur" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" msgstr "" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." msgstr "" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "Dag(ar)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "Vika(ur)." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" msgstr "" -#: ../../qt/snapshotsdialog.py:60 +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:389 +#, fuzzy +msgid "… the free space is less than" +msgstr "Um tað tøka plássið er minni enn:" + +#: qt/manageprofiles/tab_remove_retention.py:394 +#, fuzzy +msgid "… the free inodes are less than" +msgstr "Um tað tøka plássið er minni enn:" + +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Tak gamlar støðumyndir burtir" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "" + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Snarvegir" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "" + +#: qt/placeswidget.py:146 +#, fuzzy +msgid "Backup directories" +msgstr "Trygdarritingar-skjáttur" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Umhvarv: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Umhvarv: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Arbeiði…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Arbeiði…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "" + +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Vís fjaldar fílur" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Tak skjáttur við" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "" + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "" + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "" + +#: qt/snapshotsdialog.py:68 +#, fuzzy msgid "Command:" -msgstr "Stýriboð:" +msgstr "Stýriboð" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." msgstr "" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "" + +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Útseta trygaravrit ímeðan streymur ikki er tilsettur" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" msgstr "" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" msgstr "" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Far til" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Tú kannst ikki samanbera eina støðumynd við sær sjálvari" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Kostir" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Stýriboð ikki funni: %s" +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "" + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "" + +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Rót" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" +msgstr "" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Í dag" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Í gjár" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Henda vikan" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Fyrra vikan" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" diff --git a/common/po/fr.po b/common/po/fr.po index 0f7ffffae..039ddc0fe 100644 --- a/common/po/fr.po +++ b/common/po/fr.po @@ -1,1826 +1,2660 @@ -# French translation of Back In Time. -# Copyright (C) 2008-2009 Oprea Dan -# This file is distributed under the same license as the Back In Time package. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © bubu of Debian-l10n-fr +# SPDX-FileCopyrightText: © Michel Corps (@jej) +# SPDX-FileCopyrightText: © milimarg +# SPDX-FileCopyrightText: © Loufute +# SPDX-FileCopyrightText: © Iann +# SPDX-FileCopyrightText: © Over_score # +# SPDX-License-Identifier: GPL-2.0-or-later # +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: Back In Time 0.8.8\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2017-02-06 20:24+0000\n" -"Last-Translator: Jean-Marc \n" -"Language-Team: \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-06 12:48+0000\n" +"Last-Translator: Loufute \n" +"Language-Team: French \n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Échec de l'enregistrement de la configuration : %s" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Impossible de charger la configuration : %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" +"Les licences utilisées dans ce projet sont stockées dans le répertoire " +"{dir_link}. Pour extraire chaque fichier de licence et les informations de " +"copyright en utilisant les métadonnées SPDX, consulter le {readme_link}." -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Le profil \"%s\" existe déjà !" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Avertissement" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Vous ne pouvez pas supprimer le dernier profil !" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Profil principal" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Désactivée" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Local (chiffré avec EncFS)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "À chaque démarrage/re-démarrage" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (chiffré avec EncFS)" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Toutes les 5 minutes" +#: common/config.py:237 +msgid "Local" +msgstr "Local" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Toutes les 10 minutes" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Chiffré localement" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Toutes les 30 minutes" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Chiffrement" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Toutes les heures" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Toutes les 2 heures" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Clé privée SSH" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Toutes les 4 heures" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil : \"{name}\"" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Toutes les 6 heures" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Le répertoire des sauvegardes n'est pas valide." -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Toutes les 12 heures" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Au moins un répertoire doit être sélectionné pour la sauvegarde." -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Heures personnalisées" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Répertoire : {path}" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Quotidiennement" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "" +"Ce répertoire ne peut pas être inclus dans la sauvegarde, car il fait partie" +" de la destination de la sauvegarde." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "À plusieurs reprises (anacron)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" +"La valeur pour \"Supprimer la plus ancienne sauvegarde si l'espace libre est" +" inférieur à\" ({val_one}) doit être inférieure ou égale au seuil de " +"\"Avertir si l'espace libre du disque est inférieur à\" ({val_two})." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Quand le lecteur est connecté (udev)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" +"Veuillez ajuster les paramètres pour que la limite de suppression des " +"sauvegardes ne soit pas supérieure à limite d'alerte." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Chaque semaine" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "La planification Udev ne fonctionne pas avec le mode {mode}" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Chaque mois" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Échec d'écriture de la nouvelle crontab." -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Jour(s)" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron n'est pas lancé, alors que la commande crontab est pourtant disponible." +" Les sauvegardes planifiées ne seront pas exécutées. Il se pourrait que Cron" +" soit installé mais pas activé. Essayez les commandes \"systemctl enable " +"cron\" et \"systemctl start cron\", ou contactez le support de votre " +"distribution GNU/Linux pour obtenir de l'aide." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Échec de l'enregistrement de la configuration" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Échec du chargement de la configuration" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Le profil \"{name}\" existe déjà." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Le dernier profil ne peut pas être supprimé." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Impossible de monter '{command}'" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "La configuration du répertoire chiffré est introuvable." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Créer un nouveau répertoire chiffré ?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Annuler" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Semaine(s)" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Veuillez entrer à nouveau le mot de passe EncFS pour confirmation." -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Année(s)" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "Les mots de passe EncFS ne correspondent pas." -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Heure(s)" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Prendre un instantané" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Mois" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Impossible de monter le répertoire chiffré \"{command}\"" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " EXPÉRIMENTAL !" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Impossible de démonter {mountprocess} depuis {mountpoint}." -#: ../../common/config.py:129 -msgid "Local" -msgstr "Local" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"{command} non trouvé. Veuillez l'installer (par exemple avec " +"\"{installcommand}\")" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Le point de montage {mntpoint} n'est pas vide." -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Clé privée SSH" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Entrez le mot de passe pour le profil {mode} \"{profile}\" :" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Chiffré localement" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Échec d'installation de la règle Udev pour le profil {profile_id}. Le " +"service DBus '{dbus_interface}' n'était pas disponible." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Chiffrement" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Impossible de trouver l'UUID pour {path}" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH chiffré" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "ÉCHEC" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Par défaut" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Restaurer les permissions" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Terminé" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Les entrées suivantes de la liste d'inclusion ne correspondent à aucun " +"fichier ou répertoire dans la source de sauvegarde :" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Différer la sauvegarde lors du fonctionnement sur batterie" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Répertoire de sauvegarde non trouvé." -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "S'il se trouve sur un disque externe, veuillez le connecter." -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Attendre {n} seconde." +msgstr[1] "Attendre {n} secondes." -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Échec de création de la sauvegarde {snapshot_id}." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Veuillez patienter. Finalisation…" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Échec de création du répertoire." -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Enregistrement du fichier de configuration…" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Enregistrement des permissions…" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "La sauvegarde {snapshot_id} est incomplète et peut être poursuivie." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Profil principal" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "" +"Suppression du répertoire incomplet {snapshot_id} de la dernière exécution" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil : « %s »" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Échec de suppression du répertoire" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Le dossier des instantanés n'est pas valide !" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Création de la sauvegarde" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Vous devez sélectionner au moins un dossier à sauvegarder !" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Succès" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Vous ne pouvez pas inclure le dossier des sauvegardes !" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Transfert partiel en raison d'une erreur" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" -"Vous ne pouvez pas inclure un sous-dossier du dossier des sauvegardes !" +"Transfert partiel dû à la disparition de fichiers sources (voir 'man rsync')" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s n'est pas un dossier !" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' s'est terminé avec le code d'état {exit_code}" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Hôte/Utilisateur/Profile-ID ne doit pas être vide !" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Voir 'man rsync' pour plus de détails" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -"Impossible d'écrire dans : %s\n" -"Êtes-vous sûr(e) d'avoir les droits en écriture ?" +"Les codes d'état négatifs de rsync sont des numéros de signal, voir 'kill " +"-l' et 'man kill'" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" -"Le système de fichiers FAT de la destination '%(path)s' ne supporte pas les " -"liens physiques. Veuillez utiliser un système de fichier Linux natif." - -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" -"Le système de fichiers de '%(path)s' est un partage SMB. Merci de vérifier " -"que le serveur SMB distant supporte les liens symboliques ou sinon activez " -"'%(copyLinks)s' dans '%(expertOptions)s'." +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Pas de changement, nouvelle sauvegarde non nécessaire" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Copier les liens (supprime les liens symboliques)" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Échec de renommage de {new_path} en {path}." -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Options avancées" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Appliquer les règles pour supprimer les anciennes sauvegardes" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" -"Le système de fichiers de '%(path)s' est un partage SSHFS. SSHFS ne supporte " -"pas les liens physiques. Merci d'utiliser le mode 'SSH' à la place." +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Appliquer la stratégie de conservation" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Impossible de trouver crontab.\n" -"Êtes-vous sûr(e) que cron est installé ?\n" -"Si ce n'est pas le cas vous devriez désactiver les sauvegardes automatiques." +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Tentative de garder un minimum d'espace libre" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "Échec d'écriture de la nouvelle crontab" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Tentative de garder un minimum de {perc} inodes libres" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"Échec d'installation de la règle Udev pour le profil %(profile_id)s. Le " -"service DBus '%(dbus_interface)s' n'était pas disponible." +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Maintenant" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Le programme udev ne fonctionne pas avec le mode %s" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Impossible de monter {sshfs}" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Impossible de trouver l'UUID pour \"%s\"" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent introuvable. Assurez-vous qu'il soit installé." -#: ../../common/encfstools.py:86 -#, python-format +#: common/sshtools.py:489 msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" -"Impossible de monter '%(command)s' :\n" -"\n" -"%(error)s" +"Impossible de déverrouiller la clé privée SSH. Mot de passe incorrect ou non" +" disponible pour cron." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "Configuration du dossier chiffré introuvable." +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Le chemin d'accès distant existe, mais n'est pas un répertoire." -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Créer un nouveau dossier chiffré ?" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Le chemin d'accès distant n'est pas accessible en écriture." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Annuler" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Le chemin d'accès distant n'est pas exécutable." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Veuillez confirmer votre mot de passe" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Impossible de créer le chemin distant." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Le mot de passe ne correspond pas" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "L'hôte distant {host} n'accepte pas {command}" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" -"Les versions 1.7.2 et précédentes de encfs ont un bug avec l'option --" -"reverse. Veuillez mettre à jour encfs" +"La vérification des commandes sur l'hôte {host} a renvoyé une erreur " +"inconnue" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Prendre un instantané" - -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" -"Une collision de hash s'est produite dans hash_id %s. Incrémentez la valeur " -"globale hash_collision et réessayez." +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "L'hôte distant {host} ne prend pas en charge les liens physiques" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "Impossible de démonter %(proc)s depuis %(mountpoint)s" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Copie de la clé SSH publique \"{pubkey}\" sur l'hôte distant \"{host}\"." -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" -"%(proc)s non trouvé. Veuillez par exemple installer %(install_command)s" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Veuillez entrer le mot de passe pour \"{user}\"." -#: ../../common/mount.py:590 -#, python-format +#: common/tools.py:382 +#, python-brace-format msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"%(user)s n'est pas membre du groupe 'fuse'.\n" -" Lancer 'sudo adduser %(user)s fuse'. Pour appliquer les modifications " -"déconnectez-vous et reconnectez-vous.\n" -"Consultez 'man backintime' pour de plus amples instructions." +"Le système de fichiers de destination pour {path} est au format NTFS, qui a " +"des incompatibilités avec les systèmes de fichiers de style Unix." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "le point de montage %s n'est pas vide." +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "Le chemin {path} n'est pas un répertoire valable." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "Délai de montage dépassé" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Échec de création du répertoire suivant :" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Profil '%(profile)s' : Entrez le mot de passe pour %(mode)s : " +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "L'accès en écriture peut être restreint." -#: ../../common/snapshotlog.py:62 +#: common/tools.py:471 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" -"### Ce journal a été décodé avec le modèle de recherche automatique\n" -"### Si certains chemins ne sont pas décodés vous pouvez les décoder " -"manuellement avec :\n" - -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "ÉCHEC" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Restaurer les permissions :" - -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Terminé" - -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "Reporter la sauvegarde lors du fonctionnement sur batterie" +"Le système de fichiers de destination pour {path} est au format FAT, qui ne " +"prend pas en charge les liens physiques. Veuillez utiliser un système de " +"fichiers GNU/Linux natif." -#: ../../common/snapshots.py:669 +#: common/tools.py:482 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" -"Impossible de trouver le dossier des instantanés.\n" -"S'il se trouve sur un disque externe, veuillez le connecter." - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "En attente %s seconde." -msgstr[1] "En attente %s secondes." +"Le système de fichiers de destination pour {path} est un partage SMB. " +"Assurez-vous que le serveur SMB distant autorise les liens symboliques ou " +"activez \"{copyLinks}\" dans \"{expertOptions}\"." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Impossible d'effectuer la sauvegarde %s !!!" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Finalisation" - -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Impossible de créer le dossier : %s" - -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Sauvegarde du fichier de configuration…" - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Sauvegarde des permissions…" - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "…" - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "L'instantané non terminé '%s' a été trouvé et peut être poursuivi." - -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "Suppression du répertoire '%s' restant de la dernière exécution" - -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Impossible de supprimer le répertoire : %s" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Copier les liens (suivre les liens symboliques)" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Prise de l'intantané" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Options pour expert" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" -"Rien n'a changé, il n'est pas nécessaire de faire une nouvelle sauvegarde" - -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Impossible de renommer %(new_path)s en %(path)s" +"Le système de fichiers de destination pour {path} est un partage sshfs, qui " +"ne prend pas en charge les liens physiques. Veuillez utiliser le mode 'SSH' " +"à la place." -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Suppression intelligente" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Échec de création du fichier dans le répertoire suivant :" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Supprimer les anciens instantanés" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "À propos de Back In Time" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Essai de conservation d'un espace libre minimum" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Copyright :" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Essai de conservation d'un minimum de %d%% d'inodes libres" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Auteurs :" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "AVEC DES ERREURS !" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Traductions :" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Maintenant" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "" +"bubu of Debian-l10n-fr\n" +"Michel Corps (@jej)\n" +"milimarg \n" +"Loufute\n" +"Iann\n" +"Over_score" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Impossible de monter %s" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Attribution des traductions non disponible dans la langue utilisée." -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" -"Impossible de déverrouiller la clé privée SSH. Mot de passe incorrect ou mot " -"de passe non disponible pour cron." +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "ce lien" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -"L'authentification sans mot de passe pour %(user)s@%(host)s à échoué. " -"Consultez 'man backintime' pour de plus amples instructions." +"Suivez {thislink} pour consulter les contributions de traduction pour toutes" +" les langues." -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" -"Le chiffrage %(cipher)s à échoué pour %(host)s :\n" -"%(err)s" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Site web du projet" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s non trouvé dans ssh_known_hosts." +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Manuel d'utilisation" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -"Le chemin d'accès distant existe mais n'est pas un répertoire :\n" -" %s" +"Ouvrir le manuel d'utilisation dans le navigateur (en local si disponible, " +"sinon en ligne)" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" -"Le chemin distant n'est pas accessible en écriture :\n" -" %s" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Version{BOLDEND} : {version}" -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -"Le chemin distant n'est pas exécutable :\n" -" %s" +"{app_name} semble s'exécuter pour la première fois, aucune configuration " +"n'ayant été trouvée." -#: ../../common/sshtools.py:474 -#, python-format +#: qt/app.py:337 msgid "" -"Couldn't create remote path:\n" -" %s" +"Import an existing configuration from a backup location or another computer?" msgstr "" -"Impossible de créer le chemin distant :\n" -" %s" +"Importer une configuration existante à partir d'un emplacement de sauvegarde" +" ou d'un autre ordinateur ?" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "Le ping de %s a échoué. Hôte en panne ou adresse incorrecte." +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Appuyez ensuite sur OK." -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" -"L'hôte distant %(host)s ne supporte pas la commande '%(command)s' :\n" -"%(err)s\n" -"Consultez 'man backintime' pour plus d'informations" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Créer une sauvegarde" -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" -"Les commandes de vérifications sur l'hôte %(host)s ont retourné une erreur " -"inconnue :\n" -"%(err)s \n" -"Consultez 'man backintime' pour obtenir des instructions supplémentaires" +"Utiliser la date de modification et la taille pour détecter les changements " +"des fichiers." -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "L'hôte distant %s ne supporte pas les liens physiques" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Créer une sauvegarde (mode somme de contrôle)" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/app.py:490 +msgid "Use checksums for file change detection." msgstr "" -"Copie de la clé SSH publique \"%(pubkey)s\" sur l'hôte distant " -"\"%(host)s\".\n" -"Veuillez entrer le mot de passe pour \"%(user)s\" :" +"Utiliser les sommes de contrôle pour détecter les changements des fichiers." -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "Prendre un instantané avec somme de contrôle" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Mettre la sauvegarde en pause" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Utiliser la somme de contrôle pour détecter les changements" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Reprendre la sauvegarde" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "Mettre en pause la prise de l'instantané" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Arrêter la sauvegarde" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "Reprendre la prise de l'instantané" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Actualiser la liste des sauvegardes" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "Arrêter la prise de l'instantané" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Nommer la sauvegarde" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Rafraîchir la liste des instantanés" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Supprimer la sauvegarde" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Nommer l'instantané" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Afficher le journal des sauvegardes" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Supprimer l'instantané" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Voir le journal de la sauvegarde sélectionnée." -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Voir le journal des instantanés" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Ouvrir le journal de la dernière sauvegarde" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Voir la dernière entrée du journal" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Voir le journal de la dernière sauvegarde." -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Préférences" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Gérer les profils…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Éditer le script de rappel utilisateur (user-callback)" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" msgstr "Arrêt" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "Éteindre le système après l'instantané ?" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Éteindre le système après la fin de la sauvegarde." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Configurer la langue…" + +#: qt/app.py:540 msgid "Exit" msgstr "Quitter" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Aide" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "manuel : Back In Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Aide du fichier de configuration" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Afficher le manuel de Back In Time (backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Site Web" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "manuel : fichier de configuration des profils" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Afficher le manuel du fichier de configuration des profils (backintime-" +"config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Ouvrir le site de Back In Time dans le navigateur" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Notes de version" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" +"Ouvrir le journal des modifications (localement si disponible, sinon en " +"ligne)" + +#: qt/app.py:573 msgid "FAQ" msgstr "FAQ" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Ouvrir la Foire Aux Questions (FAQ) dans le navigateur" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Poser une question" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Signaler un bogue" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "À propos" +#: qt/app.py:584 +msgid "Translation" +msgstr "Traduction" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Dossier parent" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "" +"Affiche à nouveau le message au sujet de la participation à la traduction." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Afficher les fichiers cachés" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Transition de chiffrement (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Affiche à nouveau le message au sujet de la suppression d'EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "À propos" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Restaurer" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." msgstr "" "Restaurer les fichiers ou répertoires sélectionnés à leur emplacement " "d'origine." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Restaurer vers…" +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Restaurer vers …" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." msgstr "" "Restaurer les fichiers ou répertoires sélectionnés vers un nouvel " "emplacement." -#: ../../qt/app.py:234 +#: qt/app.py:615 msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" "Restaurer le répertoire actuellement affiché et tout son contenu à " "l'emplacement d'origine." -#: ../../qt/app.py:239 +#: qt/app.py:621 msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" "Restaurer le répertoire actuellement affiché et tout son contenu vers un " "nouvel emplacement." -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" -"Restaurer les fichiers ou dossiers sélectionnés.\n" -"Si ce bouton est grisé, c'est probablement parce que \"%(now)s\" est " -"sélectionné dans la liste des sauvegardes à gauche." +#: qt/app.py:624 +msgid "Up" +msgstr "Dossier parent" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Instantanés" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Afficher les fichiers cachés" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Instantané" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Comparer des sauvegardes…" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Vue" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Version candidate" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Raccourcis" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Affiche à nouveau le message de cette version candidate." -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" -"Ce répertoire n'existe pas\n" -"dans l'instantané actuellement sélectionné !" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "Ajouter aux Inclusions" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Sauvegarde" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "Ajouter aux Exclusions" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Restaurer" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Aide" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Icône de la zone de notification (system tray)" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automatique" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Icône claire" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Icône sombre" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Icônes uniquement" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Texte uniquement" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Texte sous les icônes" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Texte à côté des icônes" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" -"%(appName)s n'est pas configuré(e). Voulez vous restaurer une ancienne " -"configuration ?" +"Ce répertoire n'existe pas\n" +"dans la sauvegarde sélectionnée." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Impossible de trouver le répertoire des instantanés.\n" -"S'il se trouve sur un disque externe, veuillez connecter celui-ci puis " -"cliquer sur OK" +"Ce répertoire n'existe pas\n" +"dans la sauvegarde sélectionnée." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" "Si vous fermez cette fenêtre, Back In Time ne sera pas en mesure d'arrêter " -"votre système lorsque l'instantané sera terminé.\n" -"Voulez-vous vraiment fermer ?" +"votre système lorsque la sauvegarde sera terminée." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "En cours :" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Fermer quand même la fenêtre ?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" msgstr "Terminé, il n'y a rien à sauvegarder" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Erreur :" +#: qt/app.py:1260 +msgid "Working:" +msgstr "En cours :" + +#: qt/app.py:1267 +msgid "Working" +msgstr "En cours" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Erreur" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "Envoyé :" +msgstr "Envoyé :" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "Vitesse :" +msgstr "Vitesse :" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "Temps restant estimé :" +msgstr "ETA :" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Global" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Sauvegarde :" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Système de fichiers" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Restaurer {path}" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Répertoire personnel" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Restaurer {path} vers…" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Répertoires sauvegardés" +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Bonjour\n" +"Vous avez utilisé Back In Time en {language} un certain nombre de fois.\n" +"La traduction en {language} de la version de Back In Time que vous avez installée est désormais {perc} terminée. Quel que soit votre niveau de connaissances techniques, vous pouvez contribuer à la traduction et donc à Back In Time même.\n" +"Veuillez visiter {translation_platform_url} si vous souhaitez y participer. Pour de plus amples informations et pour toute question, veuillez visiter {back_in_time_project_website}.\n" +"Nous nous excusons pour l'interruption, ce message n'apparaîtra plus. Ce dialogue est accessible à tout moment via le menu Aide.\n" +"L'équipe Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "plateforme de traduction" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Site Web" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Votre traduction" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Dans le Fediverse chez Mastodon : {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Courriel à {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Liste de diffusion {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} sur le site web du projet." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Créer un ticket" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Ou aussi, par tout moyen à votre convenance." + +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Cette version de Back In Time est une version candidate, dont l'objectif principal est d'en vérifier la stabilité, en vue de la prochaine version officielle.\n" +"Il n'y a pas de collecte des données d'utilisation ni de télémétrie. Cependant, l'équipe de Back In Time aimerait beaucoup savoir si cette version candidate est utilisée, et s'il y a un intérêt à poursuivre la diffusion de telles versions candidates.\n" +"L'équipe vous demande donc un retour rapide de votre part, pour savoir si vous avez testé cette version, et ce même si vous n'avez pas identifié de problème. Même un retour sur un rapide essai de quelques minutes serait d'une aide importante.\n" +"Vous pouvez faire un retour de plusieurs manières :\n" +"{contact_list}\n" +"Ce message n'apparaîtra plus, mais peut être affiché à tout moment via le menu d'aide.\n" +"Merci pour votre soutien et pour l'aide apportée à l'amélioration de Back In Time !\n" +"Votre équipe Back In Time" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -"Êtes-vous sûr(e) de vouloir supprimer l'instantané :\n" -"%s" +"Il y a seulement {free} d'espace libre disponible pour la destination, ce " +"qui est plus bas que le seuil configuré {threshold}." -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Procéder à la sauvegarde ?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" -"Sauvegarder les fichiers locaux avant écrasement ou\n" -"suppression avec le suffixe '%(suffix)s'." +"Tous les fichiers plus récents situés dans {path} seront supprimés. " +"Continuer ?" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -"Les nouvelles versions des fichiers seront renommés avec le préfixe " -"'%(suffix)s' avant la restauration.\n" -"Si vous n'avez pas besoin de ces fichiers, vous pouvez les supprimer avec " -"'%(cmd)s'" +"Tous les fichiers plus récents situés dans le répertoire d'origine seront " +"supprimés. Continuer ?" -#: ../../qt/app.py:1038 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Avertissement{BOLDEND} : Supprimer des fichiers à la racine du système" +" de fichiers pourrait endommager l'ensemble de votre système." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "Supprimer les fichiers les plus récents dans le dossier d'origine" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Nom de la sauvegarde" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Supprimer cette sauvegarde ?" +msgstr[1] "Supprimer ces sauvegardes ?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" -"Restaurer les fichiers ou répertoires vers leur emplacement d'origine et\n" -"supprimer les fichiers/répertoires qui ne font pas partie de la sauvegarde.\n" -"Cela va supprimer les fichiers/répertoires qui étaient exclus lors de la " -"sauvegarde !\n" -"Soyez extrêmement prudent !!!" +"Le changement de langue ne prend effet qu'après le redémarrage de Back In " +"Time." -#: ../../qt/app.py:1083 -#, python-format +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Sauvegardes versionnées (root)" + +#: qt/backintime-qt-root.desktop:23 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"Interface facile pour sauvegardes versionnées prenant moins d'espace (mode " +"root)" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "Voulez-vous vraiment restaurer ce(s) fichiers(s) :" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Sauvegardes versionnées" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" -"Voulez-vous vraiment supprimer tous les fichiers les plus récents dans " -"'%(path)s' ?" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" +msgstr "Interface facile pour sauvegardes versionnées prenant moins d'espace" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Question" -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" -"Êtes vous sûr(e) de vouloir supprimer tous les fichiers plus récents dans le " -"répertoire d'origine ?" +"Créer des copies de sauvegarde avec le suffixe '{suffix}' avant d'écraser ou" +" de supprimer des éléments locaux." -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" -"ATTENTION : supprimer des fichiers de la racine pourrait casser tout votre " -"système !!!" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Voir le contenu actuel" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Instantané : %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Voir le contenu de l'instantané fait à %s" +"Les versions les plus récentes des fichiers seront renommées avec le suffixe" +" '{suffix}' avant la restauration. Si vous n'en avez plus besoin, vous " +"pouvez les supprimer avec la commande suivante :" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Restaurer '%s'" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Restaurer seulement les fichiers qui n'existent pas ou qui sont plus récents" +" que ceux de la destination. Utilise l'option \"{rsync_example}\"." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Restaurer '%s' vers…" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Supprimer du répertoire d'origine les éléments plus récents." -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Auteurs" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Restaurer les fichiers et répertoires sélectionnés vers leur emplacement " +"d'origine et supprimer les fichiers et répertoires qui ne font pas partie de" +" la sauvegarde. Prudence extrême requise : cette option va supprimer les " +"fichiers et répertoires qui n'étaient pas inclus dans la sauvegarde." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"Voulez-vous vraiment restaurer cet élément dans le nouveau répertoire ?" +msgstr[1] "" +"Voulez-vous vraiment restaurer ces éléments dans le nouveau répertoire ?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Voulez-vous vraiment restaurer cet élément ?" +msgstr[1] "Voulez-vous vraiment restaurer ces éléments ?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Rappel-utilisateur : \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" +"Le script de rappel utilisateur (user-callback) doit contenir un shebang sur" +" la première ligne (e.g. {example})." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Traductions" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Afficher/masquer les fichiers et répertoires cachés (Ctrl+H)" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Licence" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Ajouter aux inclusions" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Ajouter aux exclusions" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Choisir parmi les éléments fréquemment utilisés ceux à ajouter à la liste " +"d'exclusion." +msgstr[1] "" +"Choisir parmi les éléments fréquemment utilisés ceux à ajouter à la liste " +"d'exclusion." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Choisir parmi les éléments fréquemment utilisés ceux à ajouter à la liste " +"d'exclusion." +msgstr[1] "" +"Choisir parmi les éléments fréquemment utilisés ceux à ajouter à la liste " +"d'exclusion." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Configurer la langue" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Traduit à : {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Langue du système" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Utiliser la langue du système." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Voir le dernier journal de sauvegarde" + +#: qt/logviewdialog.py:63 msgid "Last Log View" msgstr "Voir le dernier journal" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "Voir le journal des instantanés" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" -msgstr "Profil :" +msgstr "Profil :" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Sauvegardes :" + +#: qt/logviewdialog.py:93 msgid "Filter:" -msgstr "Filtre :" +msgstr "Filtre :" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Erreur, [I] Information, [C] Modification" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "décoder les chemins" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Tous" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "Erreurs" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 msgid "Changes" msgstr "Modifications" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informations" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Erreurs" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Erreur, [I] Information, [C] Modification" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Information" +msgstr[1] "Informations" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "décoder les chemins" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "Échecs de transfert rsync (expérimental)" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Gérer les profils" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Modifier" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Ajouter" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Retirer" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Général" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Inclure" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Exclure" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Suppression et Conservation" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Options" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "O&ptions avancées" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Restaurer la configuration" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Nouveau profil" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Renommer le profil" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Êtes-vous sûr(e) de vouloir supprimer le profil \"{name}\" ?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Copie les fichiers pointés par les liens symboliques" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Copie les liens symboliques en tant que fichiers ou répertoires réels dans " +"la sauvegarde. Sélectionnez si tous les liens ou seulement ceux pointant " +"hors du répertoire source doivent être copiés." -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Erreur" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Cette option peut augmenter la taille de la sauvegarde." -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "Question" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Désactivé par défaut." -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "Démarrer BackinTime" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" +"Tous les liens symboliques sont remplacés par les fichiers ou répertoires " +"réels vers lesquels ils pointent. Cela augmente la taille de la sauvegarde " +"et peut entraîner le fait qu'un fichier soit sauvegardé en plusieurs " +"exemplaires." -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Traitement en cours…" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Utilise 'rsync --copy-links'." -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Aujourd'hui" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Uniquement les liens externes" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Hier" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." +msgstr "" +"Seuls les liens symboliques pointant hors du répertoire source sont " +"sauvegardés en tant que fichiers. Cela augmente la taille de la sauvegarde " +"et peut entraîner le fait qu'un fichier soit sauvegardé en plusieurs " +"exemplaires." -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Cette semaine" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Utilise 'rsync --copy-unsafe-links'." -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "La semaine dernière" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Fichiers temporaires d'éditeurs et d'Office" + +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Fichiers de sauvegarde Emacs" + +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Fichiers de sauvegarde automatique Emacs" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Fichiers d'échange (swap) Vim" + +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Fichiers temporaires Microsoft Office" + +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" msgstr "" -"Ce n'est PAS un instantané (sauvegarde) mais une vue de l'état actuel de " -"vous fichiers locaux." +"Fichiers de verrouillage LibreOffice et d'autres éditeurs OpenDocument" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "Dernière vérification %s" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniatures et images temporaires" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "Afficher le journal complet" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" +"Cache des miniatures sous GNU/Linux et les autres systèmes d'exploitation " +"(OS) de type Unix" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Modifier" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Base de données des miniatures sous Windows" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "Modifier pour le système de sauvegarde complète" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Répertoire de métadonnées sur macOS" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "Ajouter à" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Verrous spécifiques aux applications" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "Supprimer" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Fichier de verrouillage de l'application Discord" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Général" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Fichier de verrouillage de session Discord" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Mode :" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Fichier de verrouillage Mozilla Firefox et Thunderbird" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Caches et répertoires temporaires" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Cache de l'application du compte utilisateur" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Répertoire temporaire du système" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -"Attention : %(app)s utilise EncFS pour le chiffrement. Un récent " -"audit de sécurité à révélé plusieurs vecteurs d'attaque possibles le " -"concernant. Veuillez jeter un oeil à la section 'A NOTE ON SECURITY' dans " -"'man backintime'." +"Cache des paquets pour les distributions GNU/Linux (basées sur) Debian" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Dossier pour les sauvegardes" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Dépôt des applications et d'exécution (runtime) Flatpak" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "Dossier" +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Répertoires d'exécution du système (runtime)" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "Réglages SSH" +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Informations noyau et de processus" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Hôte :" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" +"Informations sur l'appareil et les autres périphériques (interface sysfs)" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Port :" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Nœuds de périphériques" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Utilisateur :" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Fichiers système d'exécution (runtime)" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Chemin :" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Autres éléments non persistants" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "Type de chiffrement :" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Liste des systèmes de fichiers actuellement montés" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Clé privée :" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Fichier d'échange système (mémoire virtuelle)" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "Fichier clé" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "Point de montage du système de fichiers virtuel GNOME" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "Créer une nouvelle clé SSH sans mot de passe." +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Objets récupérés du système de fichiers" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Mot de passe" +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Divers" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Enregistrer le Mot de passe dans le trousseau" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Répertoire de métadonnées sous Microsoft Windows" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Corbeille du compte utilisateur" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Fichiers de sauvegarde système" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Suggestion d’exclusions" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -"Mot de passe en cache pour Cron (note de sécurité : root peut lire le mot de " -"passe)" +"Choisir les éléments fréquemment utilisés à ajouter aux exclusions de " +"sauvegarde." -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Options avancées" +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Par défaut" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "Chemin complet de l'instantané : " +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Remettre la sélection prédéfinie" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Planification" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "Jour :" +msgstr "Jour :" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "Jour de la semaine :" +msgstr "Jour de la semaine :" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Heure :" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Heure :" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" -msgstr "Heures :" +msgstr "Heures :" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "de chaque heure" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minutes :" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Exécuter Back In Time dès que le lecteur est connecté (seulement une fois " +"tous les X jours). Une invitation à saisir votre mot de passe super-" +"utilisateur apparaîtra." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" -"Exécuter BackInTime à plusieurs reprises. Ce réglage est utile si " -"l'ordinateur n'est pas allumé régulièrement" +"Exécuter Back In Time à plusieurs reprises. Ce réglage est utile si " +"l'ordinateur n'est pas allumé régulièrement." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "Tous les :" +msgstr "Tous les :" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Activer l'enregistrement des messages de débogage" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" +"Écrit des messages de niveau débogage dans le journal du système via \"--" +"debug\"." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -"Exécuter Back In Time dès que le lecteur est connecté (seulement une fois " -"tous les X jours).\n" -"Vous serez invité à entrer votre mot de passe sudo." +"Attention : n'utilisez cette fonction que temporairement pour des " +"diagnostics, car elle génère une grande quantité de données." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Inclure" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Désactivée" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Inclure les fichiers et répertoires" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "À chaque démarrage/re-démarrage" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Ajouter un fichier" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Chaque minute" +msgstr[1] "Toutes les {n} minutes" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Chaque heure" +msgstr[1] "Toutes les {n} heures" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Ajouter un répertoire" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Horaires personnalisées" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Exclure" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Tous les jours" -#: ../../qt/settingsdialog.py:434 +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "À plusieurs reprises (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Quand le disque est connecté (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Chaque semaine" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Chaque mois" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Chaque année" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Heure(s)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Jour(s)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Semaine(s)" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Mois" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" -"Attention : Les caractères génériques ('foo*', '[FF]oo', 'fo?') " -"seront ignorés en mode 'SSH chiffré'.\n" -"Seuls les astérisques séparés sont autorisés ('foo/*', 'foo/**/bar')" +"Les heures personnalisées doivent être séparées par une virgule (ex : " +"8,12,18,23) ou */3 pour les sauvegardes périodiques toutes les 3 heures." -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Exclure les motifs, fichiers ou répertoires" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Máis de:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Escolla un ficheiro de chave privada existente noutro lugar." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Se o espazo libre é menor de:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "Se os inodos libres son menos de:" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Crear unha nova chave SSH sen contrasinal." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "Executar en segundo plano no servidor remoto." +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Ruta completa: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "Conservar todas as copias dos últimos" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Chave privada:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "día(s)" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Usar a configuración SSH do sistema" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "Conservar unha copia diaria dos últimos" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" +"Deixe o ficheiro de chaves sen escoller. As conexións SSH dependerán da " +"configuración do cliente existente no sistema (p. ex., ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "Conservar unha copia semanal das últimas" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Proxy SSH" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "semana(s)" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Servidor:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "Conservar unha copia mensual dos últimos" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Porto:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "mes(es)" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Usuario:" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "Conservar unha copia de seguranza anual" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." +msgstr "" +"Conéctese ao servidor de destino a través deste proxy (tamén coñecido como " +"servidor de salto). Consulte «-J» na documentación da orde «ssh» ou " +"«ProxyJump» na páxina de man «ssh_config» para obter máis detalles." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Non eliminar as copias de seguranza con nome" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Información{ENDBOLD}: no modo «Cifrado SSH», só funcionan os " +"asteriscos simples ou dobres (p. ex., {example2}). Ignoraranse outros tipos " +"de comodíns e patróns (p. ex., {example1}). Os nomes de ficheiros son " +"imprevisíbeis neste modo por mor do cifrado de EncFS." -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opcións" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Excluír os patróns, ficheiros ou directorios" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Activar as notificacións" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Engadir o patrón" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Desactivar as copias de seguranza cando estea en modo batería" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Engadir ficheiros" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "O estado da alimentación non está dispoñíbel no sistema" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Engadir directorios" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "Executar só unha copia á vez" +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Suxestións" -#: ../../qt/settingsdialog.py:625 -msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -"As demais copias bloquearanse até que remate a actual.\n" -"Esta é unha opción global. Afectará a todos os perfís deste usuario.\n" -"Pero tamén precisa activalo para o resto de usuarios." +"Seleccione entre os elementos de uso común para engadilos á lista de " +"exclusión." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "Facer copia dos ficheiros substituídos na restauración" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Excluír ficheiros maiores de:" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Continuar con erros (mantén as copias de seguranza incompletas)" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Excluír os ficheiros máis grandes que o valor en {size_unit}." -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "Facer unha nova copia aínda que non houbese cambios." +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Co «Modo rsync completo» desactivado, esta opción só afecta aos ficheiros " +"creados recentemente, xa que rsync trátao como unha opción de transferencia " +"no canto dunha regra de exclusión. En consecuencia, os ficheiros grandes dos" +" que xa se fixeron copias de seguranza permanecerán nas copias de seguranza " +"aínda que sexan modificadas." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Excluír o patrón" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Nivel de rexistro:" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Introduza un patrón de exclusión:" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Ningún" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "" +"Para obter axuda, consulte a sección da páxina de «man» de rsync {link}." + +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Abrir a páxina do «man» de rsync" + +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Excluír ficheiros" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Cambios e erros" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Excluír directorios" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Cambie estas opcións unicamente se sabe ben o que está a facer" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" +"Foi desactivado porque este patrón non funciona no modo «SSH cifrado»." + +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"Estas opcións son para configuracións avanzadas. Modifíqueas só se é " +"plenamente consciente das implicacións." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "Executar «nice»:" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Executar «rsync» con «{cmd}»:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" msgstr "como un traballo de cron" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" msgstr "nun servidor remoto" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "Executar «ionice»:" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "ao facer unha copia de seguranza manual" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "ao facer unha copia manual" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Instale «nocache» para activar esta opción." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "Executar «rsync» con «nocache»:" - -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" msgstr "na máquina local" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "Redireccionar stdout a /dev/null en cronjobs." +msgstr "Redirixir stdout a /dev/null en cronjobs." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" +"Cron enviará automaticamente un correo coa saída adxunta de cronjobs se hai " +"un MTA instalado." + +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "Redireccionar stderr a /dev/null en cronjobs." +msgstr "Redirixir stderr a /dev/null en cronjobs." + +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" +"Cron enviará automaticamente un correo con erros de cronjobs adxuntos se hai" +" un MTA instalado." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "Limitar o uso do largo de banda para rsync: " +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/s" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr " KB/seg" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Limitar o uso do largo de banda de rsync:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "Preservar permisos (ACL)" +msgstr "Preservar ACL (listas de control de acceso)" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Preservar os atributos estendidos (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" -"Copiar as ligazóns inseguras (funciona unicamente con ligazóns absolutas)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Restrinxir a un sistema de ficheiros" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "As opcións deben levar comiñas p.e. {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "Engadir as opcións adicionais a rsync" +msgstr "Pegar as opcións adicionais a rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefixo a executar antes de cada orde no servidor remoto." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -"As opcións deben levar comiñas p.e. --exclude-" -"from=\"/ruta/ao/ficheiro_excluído\"." +"As variábeis deben escaparse con \\$FOO. Isto non afecta a rsync. Polo " +"tanto, para engadir un prefixo para rsync, use «{example_value}» con " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "predeterminado" + +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" msgstr "Engadir prefixo ás ordes de SSH" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Comprobar se o servidor remoto está en liña" + +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Advertencia: se está desactivado e o servidor remoto non estiver dispoñíbel," +" poderíanse producir erros estraños." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "predeterminado" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Comprobar se o servidor remoto acepta todas as ordes necesarias." -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" -msgstr "Comprobar se o servidor remoto está en liña" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." +msgstr "" +"Advertencia: se está desactivado e o servidor remoto non admite as ordes " +"necesarias, poderíanse producir erros estraños." + +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(predeterminado: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "desactivado" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "activado" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Modo:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Onde gardar as copias de seguranza" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Axustes SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Ruta:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Ficheiro de chaves:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Contrasinal" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Gardar o contrasinal no chaveiro" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" +"Gardar o contrasinal de Cron na memoria tobo (problema de seguranza: root " +"pode ler o contrasinal)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avanzado" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Ruta completa á copia de seguranza:" + +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" +"A programación está desactivada por mor de que non se atopou ningunha " +"instalación de cron. Instale cron para activar as copias de seguranza " +"programadas." + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "A ruta de destino da copia de seguranza non pode estar baleira." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "Comprobe se o servidor remoto acepta todas as ordes" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "O contrasinal de cifrado non pode estar baleiro." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"Produciuse un erro ao intentar acceder no servidor remoto. Devolveuse a " +"seguinte mensaxe de erro:" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "Restabelecer config." +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." +msgstr "" +"Para activar o acceso sen contrasinal, a chave SSH pública pode ser copiada " +"no servidor remoto." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Quere continuar coa copia da chave SSH?" + +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Non foi posíbel copiar a chave SSH pública. Isto pode deberse a un incidente" +" de conexión ou permisos." + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Non é posíbel estabelecer a autenticidade do servidor {host}." -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "A pegada dixital da chave {keytype} é:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "" +"Verifique esta pegada dixital! Quere engadila ao ficheiro «known_hosts»?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Confirma que quere cambiar o directorio de copia de seguranza?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "O destino da copia de seguranza seleccionado non está baleiro." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Debe estar baleiro para usar o cifrado." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Ficheiro incorrecto: Non é unha chave SSH privada" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -"Facer unha copia de todo o sistema creará unha imaxe para restabelecer no " -"mesmo disco(s) físico co mesmo particionado que a orixe; o restabelecemento " -"nun novo disco físico ou no mesmo disco físico cun particionado distinto " -"resultará nunha quebra potencial e nun sistema non usábel.\n" -"\n" -"A copia de seguranza do sistema completo sobrescribirá algúns axustes " -"personalizados polo usuario. Continuamos?" +"O ficheiro seleccionado ({path}) é unha chave SSH pública. Escolla en " +"troques o ficheiro de chave privada correspondente (sen «.pub»)." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Perfil novo" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" +"Xa existe o ficheiro {path}. Non foi posíbel crear unha nova chave SSH con " +"ese nome." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Renomear o perfil" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Produciuse un fallo ao crear a nova chave SSH en {path}." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Está certo de eliminar o perfil \"%s\" ?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Incluír os ficheiros e os directorios" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" -"As horas personalizadas só poden ser unha lista de horas separadas por comas " -"(p.e: 8,12,18,23) ou */3 para copias periódicas cada 3 horas" +"«{path}» é unha ligazón simbólica. Non se fará unha copia de seguranza do " +"destino ligado ata que tamén o inclúa." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Quere incluír o destino da ligazón simbólica?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Incluír ficheiros" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Incluír directorios" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Activar as notificacións" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Desactivar as copias de seguranza cando se emprega a batería" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "O estado da alimentación non está dispoñíbel no sistema" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Executar só unha copia de seguranza á vez" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" -"Non escolleu un ficheiro coa chave privada para o SSH.\n" -"Gustaríalle xerar un par de chaves públicas/privadas sen contrasinal?" +"As outras copias de seguranza van ser bloqueadas ata que se complete a copia" +" de seguranza actual. Este é un axuste global, o que significa que afectará " +"a todos os perfís deste usuario. Mais tamén debe ser activado para o resto " +"de usuarios." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Copia de seguranza dos ficheiros substituídos na restauración" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Continuar en erros (manter as copias de seguranza incompletas)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Usar a suma de comprobación para detectar cambios" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Crear unha nova copia de seguranza, houber cambios ou non." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "Non existe o ficheiro coa chave privada «%(file)s»." +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Avisar se o espazo libre no disco cae por baixo de" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Amosa un aviso cando o espazo libre no disco de destino da copia de " +"seguranza é menor que o valor especificado." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Se a directiva de Retirada e retención está activada e as copias de " +"seguranza antigas son eliminadas en función do espazo libre dispoñíbel, este" +" valor non pode ser inferior ao valor definido na directiva." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Nivel do rexistro:" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Ningún" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "manual do usuario" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"As seguintes regras procésanse de arriba abaixo. As regras posteriores " +"anulan as anteriores. Consulte o {manual_link} para obter máis detalles e " +"exemplos." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Excluír o patrón" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Abrir o manual de usuario no navegador." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Excluír o ficheiro" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Conservar a copia de seguranza máis recente." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Excluír o cartafol" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" +"A última ou máis recente das copias de seguranza conservase en todas as " +"circunstancias." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Non é posíbel cambiar este comportamento." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Incluír o ficheiro" +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Conservar as copias de seguranza con nome." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" -"«%s» é unha ligazón simbólica. O destino ligado non se copiará até que o " -"inclúa.\n" -"Desexa incluír tamén o destino das ligazóns simbólicas?" +"As copias de seguranza ás que se lles deu un nome, ademais da marca de tempo" +" habitual, conservaranse en todas as circunstancias e non se eliminarán." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Incluír o cartafol" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Ano(s)" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Está certo de querer cambiar o cartafol de copias de seguranza?" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Retirar as copias de seguranza anteriores a" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "Produciuse un fallo creando a nova chave SSH en %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Días completos. O día actual ignorase." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "activado" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "Semanas naturais co luns como primeiro día. A semana actual ignorase." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "desactivado" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Períodos de 12 meses. O mes actual ignórase." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "Restabelecer axustes" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Directiva de retención" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr " e engadir o seu usuario ao grupo «fuse»" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Executar en segundo plano no servidor remoto." -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:313 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" -"Vaia á copia de seguranza na que desexa restabelecer a configuración de " -"%(appName)s. A ruta será como esta: \n" -" %(samplePath)s\n" -"\n" -"Se as copias de seguranza están nunha unidade remota ou están cifradas debe " -"montalas primeiro. Se usa o modo SSH pode necesitar\n" -"configurar o acceso coa chave pública ao host%(addFuse)s." +"A directiva de retención executarase directamente na máquina remota, non " +"localmente. As ordes «bash», «screen» e «flock» deben estar instaladas e " +"dispoñíbeis na máquina remota." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Se se selecciona, Back In Time probará primeiro a máquina remota." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Os días cóntanse a partir de hoxe." -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "Non se atopou config" +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Conservar todas as copias de seguranza dos últimos" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "día(s)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Conservar a última copia de seguranza de cada día para o último" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"As semanas cóntanse a partir da semana actual. Unha semana comeza o luns." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Conservar a última copia de seguranza por semana para a última" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "semana(s)." -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Os meses cóntanse como meses naturais a partir do mes en curso." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Conservar a última copia de seguranza de cada mes para o último" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "mes(es)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Os anos cóntanse como anos naturais a partir do ano en curso." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Conservar a última copia de seguranza de cada ano para" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "todos os anos." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… o espazo libre é inferior a" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… os inodos libres son menos do" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Retirar as copias de seguranza antigas se…" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "É necesaria a autenticación para executar Back In Time como «root»." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" +"É necesaria a autenticación para cambiar a programación de copias de " +"seguranza activadas pola conexión de dispositivos de almacenamento externos." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Atallos" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Lugares" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Sistema de ficheiros" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Directorios de copia de seguranza" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Perfil: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Perfil: {profile_name} (polo usuario «{desktop_user}»)" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Ver o último rexistro" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Iniciar {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "En proceso…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "Páxina de «man»: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importar a configuración" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Non foi seleccionado ningún directorio" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importar" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Buscando…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" +"Seleccione o directorio de copia de seguranza desde o que se debe importar o" +" ficheiro de configuración. A ruta pode ter este aspecto: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Se o directorio está situado nunha unidade externa ou remota, debe montarse " +"previamente de xeito manual." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Escanear de novo" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Amosar os directorios agochados" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Amosar/agochar os directorios agochados (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Non se atopou ningunha configuración neste directorio" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Completouse a busca." -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Opcións de comparación" +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Amosar o rexistro completo" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Conta atrás para o apagado" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Rematou a copia de seguranza." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Cancelar o apagado" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Apagar agora" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "O sistema apagarase e {n} segundo." +msgstr[1] "O sistema apagarase e {n} segundos." -#: ../../qt/snapshotsdialog.py:60 +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Opcións sobre a comparanza de copias de seguranza" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Orde:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parámetros:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "Usar %1 e %2 como parámetros da ruta" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Listar unicamente as copias de seguranza diferentes" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Defina unha orde diff ou prema en Cancelar." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "Listar só as copias de seguranza iguais a: " +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Non é posíbel atopar a orde «{cmd}» neste sistema. Tente outra cousa ou " +"prema en Cancelar." + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" +"Non se definiu ningún parámetro para a orde diff. Usando o valor " +"predeterminado «{params}»." -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Copias de seguranza" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Só copias de seguranza con diferenzas" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Listar só as copias de seguranza que sexan iguais a:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Comprobación exhaustiva (máis precisa pero máis lenta)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "Eliminar" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "Selecionar todo" +msgstr "Seleccionar todo" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diferenzas" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Comparar" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Ir a" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Non pode comparar unha copia de seguranza con ela mesma" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opcións" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Non é posíbel comparar unha copia de seguranza consigo mesma, xa que a " +"comparación sería redundante." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" +"Confirma que quere eliminar {file_or_dir} na copia de seguranza {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "" +"Confirma que quere eliminar {file_or_dir} en {count} copias de seguranza?" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Non se atopou a orde: %s" +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "ADVERTENCIA: isto non se pode revogar." -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Excluír {path} de copias de seguranza futuras?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Modo raíz" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -"Está certo de eliminar \"%(file)s\" na copia de seguranza " -"\"%(snapshot_id)s?\n" +"Back In Time está en execución neste momento con privilexios de raíz (root) " +"con acceso completo ao sistema" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Hoxe" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Onte" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:76 +msgid "This week" +msgstr "Esta semana" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "A semana pasada" + +#: qt/timeline.py:88 +msgid "This month" +msgstr "" + +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -"Está certo de eliminar \"%(file)s\" en %(count)d copias de seguranza?\n" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "AVISO: isto non se pode revogar" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Última comprobación {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "Quere excluír \"%s\" de copias de seguranza futuras?" +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Esta NON é unha copia de seguranza senón unha vista actual dos ficheiros " +#~ "locais." diff --git a/common/po/he.po b/common/po/he.po index 5b636ce93..8953d0558 100644 --- a/common/po/he.po +++ b/common/po/he.po @@ -1,1657 +1,2529 @@ -# Hebrew translation for backintime -# Copyright (c) 2008 Rosetta Contributors and Canonical Ltd 2008 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2008. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Yaron Shahrabani # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:25+0000\n" -"Last-Translator: Konstantin Golenberg \n" -"Language-Team: Hebrew \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-08-04 20:46+0000\n" +"Last-Translator: Yaron Shahrabani \n" +"Language-Team: Hebrew \n" +"Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 5.12.2\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"כל הרישיונות בהם המיזם הזה משתמש נמצאים בתיקייה {dir_link}. כדי לחלץ רישיון " +"ופרטי זכויות יוצרים לפי קובץ באמצעות נתוני על מסוג SPDX, יש לפנות אל " +"{readme_link}." -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "אזהרה" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "הפרופיל \"%s\" כבר קיים !" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "פרופיל ראשי" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "לא ניתן להסיר את הפרופיל האחרון !" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "מקומי (בהצפנת EncFS)" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "מנוטרל" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (בהצפנת EncFS)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "כל איתחול של מערכת" +#: common/config.py:237 +msgid "Local" +msgstr "מקומי" + +#: common/config.py:240 +msgid "Local encrypted" +msgstr "בהצפנה מקומית" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "כל 5 דקות" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "הצפנה" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "כל 10 דקות" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "כל 30 דקות" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "מפתח SSH פרטי" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "כל שעה" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "פרופיל: „{name}”" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "כל שעתיים" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "תיקיית הגיבוי שגויה." -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "כל 4 שעות" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "יש לבחור לפחות תיקייה אחת לגיבוי." -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "כל 6 שעות" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "תיקייה: {path}" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "כל 12 שעות" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "אי אפשר לכלול את התיקייה בגיבוי כיוון שהיא חלק מיעד הגיבוי עצמו." -#: ../../common/config.py:87 -msgid "Custom Hours" +#: common/config.py:366 +#, fuzzy, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"הערך עבור „הסרת הגיבוי הישן ביותר אם המקום הפנוי קטן מאשר” ({val_one}) חייב " +"להיות קטן או שווה לסף „להזהיר אם המקום הפנוי יורד מתחת ל־” ({val_two})." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "כל יום" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "נא לכוון את ההגדרות כדי שהסרת הגיבויים לא עולה על מגבלת האזהרה." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "תזמון udev לא עובד עם המצב {mode}" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "כתיבת crontab חדש נכשלה." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "כל שבוע" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron לא פעיל למרות שהפקודה crontab זמינה. משימות גיבוי מתוזמנות לא תרוצנה. " +"כנראה ש־Cron מותקן אבל לא מופעל. כדאי לנסות להריץ את שתי הפקודות „systemctl " +"enable cron” ואת „systemctl start cron” או להיוועץ בערוצי התמיכה של הפצת " +"הגנו/לינוקס שלך לקבלת עזרה." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "שמירת ההגדרות נכשלה" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "טעינת ההגדרות נכשלה" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "הפרופיל „{name}” כבר קיים." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "אי אפשר להסיר את הפרופיל האחרון." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "אי אפשר לעגן את ‚{command}’" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "לא נמצאו הגדרות לתיקייה מוצפנת." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "ליצור תיקייה מוצפנת חדשה?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "ביטול" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "כל חודש" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "נא למלא את סיסמת ה־EncFS מחדש כדי לאשר." -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "יום/ימים" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "סיסמאות ה־EncFS לא תואמות." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "שבוע/ות" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "שמירת תמונת מצב" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "שנה/שנים" +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "אי אפשר לעגן את ‚{command}’" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "לא ניתן לנתק את העיגון {mountprocess} מ־{mountpoint}." -#: ../../common/config.py:105 -msgid "Month(s)" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" +"פקודה {command} לא נמצאה. אפשר להתקין את הפקודה (בעזרת \"{installcommand}\")" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "נקודת העיגון {mntpoint} לא ריקה." -#: ../../common/config.py:129 -msgid "Local" -msgstr "מקומי" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "הכנס סיסמא בשביל {mode} משתמש \"{profile}\":" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"לא ניתן להתקין כלל Udev לפרופיל {profile_id}. שירות ה־D-Bus‏ " +"‚{dbus_interface}’ לא היה זמין." -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "מפתח פרטי SSH" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "לא ניתן למצוא מזהה ייחודי ל־{path}" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "נכשל" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "שחזור הרשאות" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "בוצע" -#: ../../common/config.py:135 -msgid "Default" -msgstr "ברירת מחדל" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "לרשומות הבאות מרשימת ההכללה אין קובץ או תיקייה תואמים במקור הגיבוי:" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "הגיבוי מושהה בעת שימוש בסוללה" + +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "לא ניתן למצוא את תיקיית הגיבויים." + +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "אם היא נמצאת על כונן נשלף, יש לחבר אותו." + +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "בהמתנה של שנייה אחת." +msgstr[1] "בהמתנה של {n} שניות." + +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "יצירת הגיבוי {snapshot_id} נכשלה." + +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "נא להתאזר בסבלנות. התהליך מסתיים…" + +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "לא ניתן ליצור את התיקייה." + +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "קובץ ההגדרות נשמר…" + +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "הרשאות נשמרות…" + +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "נמצאו שאריות של הגיבוי {snapshot_id} שאפשר להמשיך." + +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "התיקייה {snapshot_id} החלקית שנשארה מהריצה האחרונה נמחקת" + +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "לא ניתן להסיר תיקייה" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "נוצר גיבוי" + +#: common/snapshots.py:1517 +msgid "Success" +msgstr "הצלחה" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "שגיאה: בוצעה העברה חלקית בלבד" + +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "העברה חלקית עקב קובצי מקור שנעלמו (ר׳ ‚man rsync’)" + +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "‚rsync’ הסתיים עם קוד היציאה {exit_code}" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "קרא את 'man rsync' לפרטים נוספים" + +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "" +"קודים שליליים ביציאה של rsync הם מספרי אותות, ר׳ ‚kill -l’ ו־‚man kill’" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "דבר לא השתנה, לא צריך גיבוש חדש" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "לא ניתן לשנות את השם {new_path} ל־{path}." -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "החלת כללים להסרת גיבויים ישנים" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "החלת מדיניות שימור" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "מתבצע ניסיון לשמור על מקום פנוי מזערי" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "מתבצע ניסיון לשמור לפחות {perc} מה־inodes פנויים" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "עכשיו" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "לא ניתן לעגן את {sshfs}" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent לא נמצא. נא לוודא שהוא מותקן." -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "" +"לא ניתן לשחרר את נעילת מפתח ה־ssh הפרטי. הסיסמה שגויה או שהסיסמה לא זמינה " +"ל־cron." -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "הנתיב המרוחק קיים אך הוא לא תיקייה." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "הפרופיל הראשי" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "לא ניתן לכתוב לנתיב המרוחק." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "הפרופיל: \"%s\"" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "הנתיב המרוחק לא ניתן להפעלה." -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "תיקיית הגיבויים אינה תקנית !" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "לא ניתן ליצור את הנתיב המרוחק." -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "עליך לבחור לפחות תיקייה אחת לגיבוי !" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "מארח מרוחק {host} לא תומך בפקודה {command}" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "לא ניתן לכלול את תיקיית הגיבוי !" +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "פקודות הבדיקה שרצו על {host} החזירו שגיאה לא ידועה" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "לא ניתן לכלול תת־תיקיות של תיקיית הגיבוי !" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "המארח המרוחק {host} לא תומך בקישורים קשיחים" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s אינה תיקייה !" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "העתקת מפתח ה־SSH הציבורי „{pubkey}” למארח המרוחק „{host}”." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "נא למלא סיסמה למשתמש „{user}”." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format +#: common/tools.py:382 +#, python-brace-format msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"לא ניתן לכתוב אל: %s\n" -"האם יש ברשותך גישה לכתיבה ?" +"מערכת הקבצים המיועדת ל־{path} מפורמטת ל־NTFS, שידועה בחוסר התאימות שלה " +"למערכות קבצים תואמות יוניקס." -#: ../../common/config.py:402 -#, python-format +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} אינה תיקייה תקפה." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "יצירת התיקייה הבאה נכשלה:" + +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "גישת הכתיבה כנראה מוגבלת." + +#: common/tools.py:471 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"מערכת הקבצים המיועדת ל־{path} מפורמטת ל־FAT שבה אין תמיכה בקישורים קשיחים. " +"נא להשתמש במערכת קבצים טבעית של גנו/לינוקס." -#: ../../common/config.py:407 -#, python-format +#: common/tools.py:482 +#, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"מערכת היעד עבור {path} היא שיתוף SMB מעוגן. נא לוודא ששרת ה־SMB המרוחק תומך " +"בקישורים סמליים או להפעיל את„ {copyLinks}” תחת „{expertOptions}”." -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 +#: common/tools.py:486 msgid "Copy links (dereference symbolic links)" -msgstr "" +msgstr "העתקת קישורים (ביטול הפניה של קישורים סמליים)" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 +#: common/tools.py:487 msgid "Expert Options" msgstr "אפשרויות מתקדמות" -#: ../../common/config.py:413 -#, python-format +#: common/tools.py:491 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"מערכת הקבצים המיועדת עבור {path} היא שיתוף בעיגון sshfs. ב־sshfs אין תמיכה " +"בקישורים קשיחים. נא להשתמש במצב „SSH” במקום." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"לא ניתן למצוא את crontab.\n" -"האם cron מותקן?\n" -"במידה שלא יש לבטל את כל הגיבויים האוטומטיים." +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "יצירת קבצים בתיקייה הזאת נכשלה:" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "על חזרה בזמן" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "זכויות יוצרים:" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "יוצרים:" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "מתרגמים:" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Yaron Shahrabani" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "אין תודות מתרגמים לשפה הנוכחית." -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "לקישור הזה" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "אפשר לגשת {thislink} כדי לצפות בתודות למתרגמים בכל השפות." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "אתר המיזם" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "מדריך למשתמשים" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" +#: qt/aboutdlg.py:247 qt/app.py:546 +#, fuzzy +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "פתיחת מדריך המשתמש בדפדפן (מקומי אם זמין או מקוון אם לא)" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "ביצוע הגיבוי" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}גרסה{BOLDEND}: {version}" -#: ../../common/mount.py:415 -#, python-format +#: qt/app.py:332 +#, fuzzy, python-brace-format msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" - -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +"{app_name} appears to be running for the first time because no configuration" +" is found." +msgstr "נראה כי {app_name} עולה לראשונה כיוון שלא נמצאו הגדרות." -#: ../../common/mount.py:590 -#, python-format +#: qt/app.py:337 +#, fuzzy msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" +"Import an existing configuration from a backup location or another computer?" +msgstr "לייבא הגדרות קיימות (מתיקיית יעד גיבוי או ממחשב אחר)?" -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "ואז ללחוץ על אישור." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "יצירת גיבוי" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "להשתמש בזמן השינוי ובגודל לזיהוי שינויים בקובץ." -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "יצירת גיבוי (מצב סיכומי ביקורת)" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "נכשל" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "להשתמש בסיכומי ביקורת לאיתור שינויים." -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "השהיית תהליך גיבוי" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "בוצע" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "המשך תהליך גיבוי" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "עצירת תהליך הגיבוי" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" -"לא ניתן למצוא את תיקיית הגיבויים.\n" -"אם היא נמצאת בכונן נשלף עליך לחבר אותו." +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "רענון רשימת הגיבויים" -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "בהמתנה של שניה אחת." -msgstr[1] "בהמתנה של %s שניות." +#: qt/app.py:508 +msgid "Name backup" +msgstr "מתן שם לגיבוי" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "נכשל בלקיחת גיבוי %s !!!" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "הסרת גיבוי" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "בהליכי סיום" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "פתיחת יומן גיבוי" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "לא ניתן ליצור את התיקייה: %s" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "הצגת יומן הגיבוי הנבחר." -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "שומר קובץ הגדרות ..." +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "פתיחת יומן הגיבוי האחרון" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "שמירת ההרשאות ..." +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "הצגת יומן הגיבוי האחרון." -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "ניהול פרופילים…" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "עריכת משוב משתמש" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/app.py:532 +msgid "Shutdown" +msgstr "כיבוי" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "לא ניתן להסיר את התיקייה: %s" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "לכבות את המערכת אחרי ששמירת הגיבוי הסתיימה." -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "ביצוע הגיבוי" +#: qt/app.py:536 +msgid "Setup language…" +msgstr "הגדרות שפה…" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: qt/app.py:540 +msgid "Exit" +msgstr "יציאה" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "עמוד ה־man‏: Back In Time" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "הסרה חכמה" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "הצגת עמוד man על Back In Time ‏(backintime)" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "הסרת גיבויים ישנים" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "עמוד man: קובץ הגדרת פרופילים" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "נסיון לשמירת כמה שפחות מקום פנוי" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "הצגת עמוד man על קובץ הגדרת פרופילים (backintime-config)" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "פתיחת אתר Back In Time בדפדפן" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "עם שגיאות !" +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "יומן שינויים" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "כעת" +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "פתיחת יומן השינויים (מקומי אם זמין או מקוון אם לא)" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/app.py:573 +msgid "FAQ" +msgstr "שאלות ותשובות" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "פתיחת שאלות נפוצות (שו״ת) בדפדפן" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "פרסום שאלה" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "דיווח על תקלה" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s לא נמצא ברשימה ssh_known_hosts." +#: qt/app.py:584 +msgid "Translation" +msgstr "תרגום" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "הצגת ההודעה בנוגע להשתתפות בתרגום שוב." -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "תעבורה מוצפנת (EncFS)" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "הצגת ההודעה על הסרת EncFS שוב." -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:599 +msgid "About" +msgstr "על אודות" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "שחזור" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "שחזור הקבצים או התיקיות הנבחרים ליעד המקורי." -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "שחזור אל…" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "שחזור הקבצים או התיקיות הנבחרים ליעד חדש." -#: ../../common/sshtools.py:761 -#, python-format +#: qt/app.py:615 +#, fuzzy msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" - -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" - -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "שחזור התיקייה שמופיעה ואת כל התוכן שלה ליעד המקורי." -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "שחזור התיקייה שמופיע ואת כל התוכן שלה ליעד חדש." -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "למעלה" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "הצגת קבצים נסתרים" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "לרענן רשימת גיבוים" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "השוואת גיבויים…" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "שם הגיבוי" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "מועמדת להוצאה לאור" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "הסר לכידה" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "הצגת ההודעה על המועמדת להוצאה לאור שוב." -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "&חזרה בזמן" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&גיבוי" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "הגדרות" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&שחזור" -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "ע&זרה" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "יציאה" - -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "עזרה" - -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "אתר הבית" - -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" +#: qt/app.py:784 +msgid "Light icon" msgstr "" -#: ../../qt/app.py:167 -msgid "FAQ" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "שאל שאלה" - -#: ../../qt/app.py:171 -msgid "Report a bug" -msgstr "דיווח על תקלה" - -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "אודות" +#: qt/app.py:808 +msgid "Icons only" +msgstr "סמלים בלבד" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "מעלה" +#: qt/app.py:811 +msgid "Text only" +msgstr "טקסט בלבד" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "הצג קבצים מוסתרים" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "טקסט מתחת לסמלים" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "שחזור" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "טקסט ליד הסמלים" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"התיקייה הזאת לא קיימת\n" +"בגיבוי הנוכחי שנבחר." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "לשחזר ל..." - -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"התיקייה הזאת לא קיימת\n" +"בגיבוי הנוכחי שנבחר." -#: ../../qt/app.py:234 +#: qt/app.py:995 msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"אם החלון הזה סגור, לא תהיה ל־Back In Time לכבות את המערכת שלך עם סיום " +"הגיבוי." -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "לסגור את החלון הזה בכל זאת?" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "הסתיים, לא נדרש גיבוי" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "גיבויים" +#: qt/app.py:1260 +msgid "Working:" +msgstr "בעבודה:" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:1267 +msgid "Working" +msgstr "בעבודה" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "שגיאה" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "קיצורים" +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "נשלחו:" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "מהירות:" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "זמן משוער לסיום:" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "גיבוי:" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" -msgstr "" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "שחזור {path}" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" -msgstr "" -"לא ניתן למצוא את תיקיית הגיבויים.\n" -"אם היא נמצאת על כונן נשלף יש לחבר אותו וללחוץ על אישור" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "לשחזר את {path} אל…" -#: ../../qt/app.py:527 +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"שלום\n" +"השתמשת ב־Back In Time ב{language} מספר פעמים עד כה.\n" +"מלאכת התרגום של הגרסה שהתקנת של Back In Time ל{language} הושלמה ב־{perc} אחוז. בלי קשר לרמת הידע הטכני שלך, אפשר לתרום למלאכת התרגום ובכך לתרום ל־Back In Time עצמה.\n" +"נא לגשת אל {translation_platform_url} כדי לעזור. לעזרה ושאלות נוספות, נא לבקר ב־{back_in_time_project_website}.\n" +"אנחנו מתנצלים על ההפרעה, וההודעה הזאת לא תופיע שוב. החלונית הזאת זמינה בכל עת דרך תפריט העזרה.\n" +"צוות Back In Time לרשותכם" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "פלטפורמת תרגום" + +#: qt/app.py:1658 +msgid "Website" +msgstr "אתר" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "התרגום שלך" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "בפדיברס דרך מסטודון: {link_and_label}." + +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "רשימת הדיוור {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "רשימת הדיוור {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} באתר המיזם." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "לפתוח דיווח תקלה" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "לחלופין, אפשר להשתמש בכל ערוץ אחר לשיקולך." + +#: qt/app.py:1731 +#, python-brace-format msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"הגרסה הזאת של Back In Time היא מועמדת להוצאה לאור ומיועדת בעיקר לבדיקות יציבות ולהכנה למהדורה הרשמית הבאה.\n" +"לא נאספים נתוני משתמש או מדדים. עם זאת, צוות Back In Time מאוד ישמח לדעת מה אופן השימוש במהדורה שמיועדת להוצאה לאור והאם שווה להמשיך לספק גרסאות טרום הוצאה לאור שכאלה.\n" +"לכן, הצוות מבקש בצורה נעימה לקבל משוב קצר על האם בדקת את הגרסה הזאת, אפילו אם לא נתקלת בתקלות כלשהן. אפילו בדיקה זריזה שתרוץ במשך מספר דקות תעזור לנו מאוד.\n" +"אלו האפשרויות הזמינות ליצירת קשר:\n" +"{contact_list}\n" +"בגרסה הזאת, ההודעה הזאת לא תופיע שוב אבל אפשר לגשת אליה בכל עת דרך תפריט העזרה.\n" +"תודה על התמיכה ועל הסיוע במאמצים לשיפור Back In Time!\n" +"צוות Back In Time לרשותך" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"רק {free} של מקום פנוי זמינים ביעד, שזה מתחת ליעד שהוגדר על סך {threshold}." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "בעבודה:" - -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "הסתיים, לא נדרש גיבוי" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "להמשיך עם הגיבוי?" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "שגיאה:" - -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" -msgstr "" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "כל הקבצים החדשים יותר ב־{path} יוסרו. להמשיך?" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" -msgstr "" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "כל הקבצים החדשים ביותר בתיקייה המקומית יוסרו. להמשיך?" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" +#: qt/app.py:1875 +#, python-brace-format +msgid "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}אזהרה{BOLDEND}: מחיקת קבצים בשורש מערכת הקבצים יכולה לשבש את כל המערכת" +" שלך." -#: ../../qt/app.py:794 -msgid "Global" -msgstr "כללי" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "תיקיית העל" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "שם הגיבוי" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "תיקיית הבית" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "להסיר את הגיבוי הזה?" +msgstr[1] "להסיר את הגיבויים האלה?" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "תיקיות הגיבוי" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "הגדרות השפה ייכנסו לתוקף רק לאחר הפעלת Back In Time מחדש." -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" msgstr "" -"אתה בטוח שברצונך להסיר את הגיבוי:\n" -"%s" -#: ../../qt/app.py:1025 -#, python-format +#: qt/backintime-qt-root.desktop:23 msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "להשאיר גיבויים עם שמות." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "שאלה" -#: ../../qt/app.py:1072 +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"ליצור עותקי גיבוי עם {suffix} כסיומת בטרם שכתוב או הסרה של רכיבים מקומיים." -#: ../../qt/app.py:1083 -#, python-format +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"לפני השחזור, גרסאות חדשות יותר של קבצים ישנות את שמם עם תוספת {suffix}. אפשר" +" להסיר את הקבצים האלה עם הפקודה הבאה:" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"לשחזר רק קבצים שלא נמצאים או שהם חדשים יותר מאלו ביעד. באמצעות האפשרות " +"„{rsync_example}”." -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "הסרת רכיבים חדשים יותר בתיקייה המקורית." -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:135 msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" - -#: ../../qt/app.py:1105 +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"שחזור הקבצים או התיקיות הנבחרים ליעדם המקורי ולמחוק קבצים ותיקיות שאינם " +"בגיבוי. חובה לנקוט במשנה זהירות כיוון שהפעולה הזאת תמחק קבצים ותיקיות " +"שהוחרגו במהלך הכנת הגיבוי." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "לשחזר את הרכיב הזה לתיקייה החדשה?" +msgstr[1] "לשחזר את הרכיבים האלה לתיקייה החדשה?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "לשחזר את הרכיב הזה?" +msgstr[1] "לשחזר את הרכיבים האלה?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "קריאת משתמש: „{filename}”" + +#: qt/editusercallback.py:105 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"סקריפט קריאת משתמש חייב לכלול שורת פתיח (shebang) בשורה הראשונה (למשל: " +"{example})." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "צפיה בתוכן הנוכחי של הכונן" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "הצגת/הסתרת קבצים ותיקיות מוסתרים (Ctrl+H)" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "גיבוי: %s" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "הוספה להכללה" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "צפיה בגיבוי שבוצע ב־%s" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "הוספה להחרגה" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "הגדרת שפה" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "תורגמו: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "ברירת מחדל המערכת" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "להשתמש בשפת מערכת ההפעלה." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "תצוגת יומן גיבוי" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "תצוגת היומן האחרון" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "לשחזר '%s'" +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "פרופיל:" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "לשחזר '%s' ל..." +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "גיבויים:" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "סינון:" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] שגיאה, [I] מידע, [C] שינוי" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "פענוח נתיבים" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "הכול" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "שינויים" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "פרופיל:" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "שגיאות" -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "סינון:" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "פרטים" +msgstr[1] "פרטים" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "הכל" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "כשלי העברה דרך rsync (ניסיוניים)" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "שגיאות" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "ניהול פרופילים" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "שינויים" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "עריכה" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "הוספה" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "הסרה" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&כללי" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "לכלול" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "לה&חריג" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "ה&סרה ושימור" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&אפשרויות" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "אפשרויות &מומחים" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "שחזור הגדרות" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "פרופיל חדש" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "שינוי שם לפרופיל" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "למחוק את הפרופיל „{name}”?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." msgstr "" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "פועל..." +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "היום" +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "השהיית תהליך גיבוי" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "אתמול" +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "החרגת קבצים" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "השבוע" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "שבוע שעבר" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" msgstr "" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "עריכה" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "כללי" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "מצב:" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "תיקיות גיבוי" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "איפה לשמור גיבויים" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "לא ניתן להסיר תיקייה" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "הגדרות SSH" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "הצגת תיקיות מוסתרות" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "פורט:" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "צופן:" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "מפתח פרטי:" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "סיסמה" +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "הגדרות מתקדמות" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "עצירת תהליך הגיבוי" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "החרגת תיקיות" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "ברירת מחדל" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "תזמון" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "יום:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "" +msgstr "יום בשבוע:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" msgstr "שעה:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "שעות:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "אחרי השעה" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "דקות:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"להריץ את Back In Time עם חיבור הכונן (פעם אחת כל X ימים). תופיע בקשה לסיסמת " +"העל שלך (sudo)." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." -msgstr "" +msgstr "הרצת Back In Time באופן מחזורי. שימוש אם המחשב לא דולק קבוע." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "" +msgstr "כל:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "הפעלת תיעוד של הודעות ניפוי שגיאות" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "כתיבת הודעות ברמת ניפוי שגיאות ליומן המערכת דרך „‎--debug”." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "אזהרה: להשתמש בזה באופן זמני לאבחון, כיוון שזה מייצר המון פלט." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "כלול" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "מושבת" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "בכל טעינה/הפעלה מחדש של מערכת" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "כל דקה" +msgstr[1] "כל {n} דקות" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "כל שעה" +msgstr[1] "כל {n} שעות" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "הוספת קובץ" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "שעות מותאמות אישית" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "הוספת תיקייה" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "כל יום" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "כלול" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "במחזוריות (anacron)" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "כאשר כונן מתחבר (udev)" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "כל שבוע" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "מאוד מומלץ:" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "כל חודש" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "כל שנה" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "שעה/ות" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "יום/ימים" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "שבוע/ות" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "חודש/ים" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"שעות מותאמות אישית יכול להיות רק רשימת של שעות מופרדת בפסיקים (למשל: " +"8,12,18,23) או ‎*/3 לגיבויים במחזורים של 3 שעות." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "הסרה-אוטומטית" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." msgstr "" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" msgstr "" -#: ../../qt/app.py:1083 -#, python-format +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "Privatni ključ za SSH" + +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:212 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" msgstr "" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Domaćin:" + +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" msgstr "" -#: ../../qt/app.py:1101 +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Korisnik:" + +#: qt/manageprofiles/sshproxywidget.py:71 msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" -#: ../../qt/app.py:1105 +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Prikaži trenutni sadržaj diska" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Snimka: %s" +#: qt/manageprofiles/tab_exclude.py:85 +#, fuzzy +msgid "Exclude patterns, files or directories" +msgstr "Iskljući sljedove, datoteke ili mape" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Prikaži snimku napravljenu %s" +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Isključi slijed" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Dodaj datoteku" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Dodaj mapu" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" msgstr "" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Isključi datoteku:" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." msgstr "" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "Profil:" - -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "Filter:" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Isključi slijed" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "Sve" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Isključi slijed" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "Grješke" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Promjene" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informacije" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Isključi datoteku" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Grješka, [I] Informacije, [C] Promjeni" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Isključi mapu" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" msgstr "" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." msgstr "" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Obrađujem..." - -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Danas" +#: qt/manageprofiles/tab_expert_options.py:116 +msgid "on local machine" +msgstr "" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Jučer" +#: qt/manageprofiles/tab_expert_options.py:130 +msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Ovaj tjedan" +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Prošli tjedan" +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." +msgstr "" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Uredi" +#: qt/manageprofiles/tab_expert_options.py:204 +msgid "Preserve ACL" +msgstr "Sačuvaj ACL" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/tab_expert_options.py:222 +msgid "Preserve extended attributes (xattr)" msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Općenito" +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Gdje sačuvati snimke" +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" msgstr "" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Domaćin:" +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." +msgstr "" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Korisnik:" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." +msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Gdje sačuvati snimke" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "Novi profil" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 msgid "Password" msgstr "" -#: ../../qt/settingsdialog.py:255 +#: qt/manageprofiles/tab_general.py:206 msgid "Save Password to Keyring" msgstr "" -#: ../../qt/settingsdialog.py:258 +#: qt/manageprofiles/tab_general.py:210 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" -#: ../../qt/settingsdialog.py:270 +#: qt/manageprofiles/tab_general.py:226 msgid "Advanced" msgstr "Napredno" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" msgstr "" -#: ../../qt/settingsdialog.py:307 -msgid "Schedule" -msgstr "Raspored" - -#: ../../qt/settingsdialog.py:317 -msgid "Day:" +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" -#: ../../qt/settingsdialog.py:328 -msgid "Weekday:" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." msgstr "" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Sat:" - -#: ../../qt/settingsdialog.py:350 -msgid "Hours:" -msgstr "" +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Lozinka se ne podudara." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Run Back In Time repeatedly. This is useful if the computer is not running " -"regularly." -msgstr "" - -#: ../../qt/settingsdialog.py:364 -msgid "Every:" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Uključi" - -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Ukljući mape i datoteke" - -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Dodaj datoteku" - -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Dodaj mapu" - -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Isključi" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "" -#: ../../qt/settingsdialog.py:434 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Iskljući sljedove, datoteke ili mape" - -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Visoko preporučeno" - -#: ../../qt/settingsdialog.py:476 -msgid "Add default" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." msgstr "" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" msgstr "" -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Auto-ukloni" +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Stvori novu šifriranu mapu?" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Starije od:" - -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Ako je slobodan prostor manji od:" - -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" - -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." msgstr "" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "Privatni ključ za SSH" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." msgstr "" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/tab_include.py:48 +#, fuzzy +msgid "Include files and directories" +msgstr "Ukljući mape i datoteke" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Nemoj uklanjati imenovane snimke" +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Uključi datoteku" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opcije" +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Uključi mapu" -#: ../../qt/settingsdialog.py:615 +#: qt/manageprofiles/tab_options.py:39 msgid "Enable notifications" msgstr "Omogući obavijesti" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" msgstr "Onesposobi snimke tijekom rada na bateriji" -#: ../../qt/settingsdialog.py:621 +#: qt/manageprofiles/tab_options.py:49 msgid "Power status not available from system" msgstr "Stanje energije nije dostupno od sustava" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" msgstr "" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_options.py:56 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" -#: ../../qt/settingsdialog.py:630 +#: qt/manageprofiles/tab_options.py:63 msgid "Backup replaced files on restore" msgstr "" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" +#: qt/manageprofiles/tab_options.py:78 +#, fuzzy +msgid "Continue on errors (keep incomplete backups)" msgstr "Nastavi ne grješkama (zadrži nepotpune snimke)" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "" + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." +msgstr "" + +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:653 +#: qt/manageprofiles/tab_options.py:122 msgid "Log Level:" msgstr "Razina Zapisa:" -#: ../../qt/settingsdialog.py:658 +#: qt/manageprofiles/tab_options.py:185 msgid "None" msgstr "Nijedno" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Promjene & Grješke" - -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Mjenjajte ove opcije samo ako stvarno znate što radite !" - -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" msgstr "" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." msgstr "" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Nemoj uklanjati imenovane snimke." -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." msgstr "" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." msgstr "" -#: ../../qt/settingsdialog.py:714 -msgid "on local machine" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Nemoj uklanjati imenovane snimke." -#: ../../qt/settingsdialog.py:721 -msgid "Redirect stdout to /dev/null in cronjobs." +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Godina" -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Ukloni Snimku" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." msgstr "" -#: ../../qt/settingsdialog.py:774 -msgid "Preserve ACL" -msgstr "Sačuvaj ACL" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" -#: ../../qt/settingsdialog.py:787 -msgid "Preserve extended attributes (xattr)" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." msgstr "" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" msgstr "" -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." msgstr "" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_remove_retention.py:313 msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." msgstr "" -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." msgstr "" -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" msgstr "" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "Dan(a)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" msgstr "" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_remove_retention.py:344 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "Tjedan(a)." + +#: qt/manageprofiles/tab_remove_retention.py:357 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"The months are counted as calendar months starting with the current month." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" msgstr "" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_remove_retention.py:370 msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The years are counted as calendar years starting with the current year." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Novi profil" +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Preimenuj profil" +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Jeste li sigurni da želite obrisati profil \"%s\" ?" +#: qt/manageprofiles/tab_remove_retention.py:389 +#, fuzzy +msgid "… the free space is less than" +msgstr "Ako je slobodan prostor manji od:" -#: ../../qt/settingsdialog.py:1201 -msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:394 +#, fuzzy +msgid "… the free inodes are less than" +msgstr "Ako je slobodan prostor manji od:" -#: ../../qt/settingsdialog.py:1237 -msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Ukloni stare snimke" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1360 +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" -#: ../../qt/settingsdialog.py:1376 -#, python-format -msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Prečaci" + +#: qt/placeswidget.py:96 +msgid "Places" msgstr "" -#: ../../qt/settingsdialog.py:1384 -msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +#: qt/placeswidget.py:97 +msgid "File System" msgstr "" -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Isključi slijed" +#: qt/placeswidget.py:146 +#, fuzzy +msgid "Backup directories" +msgstr "Mape rezerve" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Isključi datoteku" +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: \"{profile_name}\"" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Isključi mapu" +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: \"{profile_name}\"" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Uključi datoteku" +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Prikaži Zadnji Zapis" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format -msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" msgstr "" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Uključi mapu" - -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Jeste li sigurni da želite promjeniti mapu snimki?" +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Radim…" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" msgstr "" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/restoreconfigdialog.py:134 +msgid "Import" msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Radim…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/restoreconfigdialog.py:216 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" msgstr "" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Prikaži skrivene datoteke" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Ukljući mape i datoteke" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Neuspješno stvaranje datoteke u sljedećoj mapi:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "" + +#: qt/restoredialog.py:58 +msgid "Show full Log" msgstr "" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Isključi sustav kad snimanje završi." + +#: qt/shutdowndlg.py:36 +#, fuzzy +msgid "Cancel Shutdown" +msgstr "Isključi računalo" + +#: qt/shutdowndlg.py:37 +#, fuzzy +msgid "Shutdown Now" +msgstr "Isključi računalo" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" msgstr "" -#: ../../qt/snapshotsdialog.py:60 +#: qt/snapshotsdialog.py:68 +#, fuzzy msgid "Command:" -msgstr "Naredba:" +msgstr "Naredba" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametri:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Prikaži samo različite snimke" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "" + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +#, fuzzy +msgid "Backups" +msgstr "&Backup" + +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Odgoda sigurnosnog kopiranja za vrijeme punjenja" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" msgstr "" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Dubinska provjera (preciznije, ali sporije)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" msgstr "" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Razlika" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Idi Na" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Nije moguće usporediti snimku s njom samom" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opcije" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" + +#: qt/snapshotsdialog.py:470 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Neuspjelo uzimanje snimke {snapshot_id} !!!" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "" + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Naredba nije pronađena: %s" +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Korijen" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" +msgstr "" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Danas" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Jučer" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Ovaj tjedan" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Prošli tjedan" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" diff --git a/common/po/hu.po b/common/po/hu.po index 6f6a12d97..52189a03a 100644 --- a/common/po/hu.po +++ b/common/po/hu.po @@ -1,1712 +1,2644 @@ -# Hungarian translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Úr Balázs , 2024, 2025, 2026. # -# FIRST AUTHOR , 2009. -# Kósa Lajos , 2013. +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-10-30 18:01+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Hungarian \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-08 06:18+0000\n" +"Last-Translator: urbalazs \n" +"Language-Team: Hungarian \n" +"Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" -"Language: hu\n" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"A projektben használt összes licenc megtalálható a(z) {dir_link} " +"könyvtárban. Az SPDX metaadatok használatával történő fájlonkénti licenc- és" +" szerzői jogi információk kinyeréséhez olvassa el a tájékoztatót: " +"{readme_link}." -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "A \"%s\" már létezik!" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Nem távolíthatod el az utolsó profilt!" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Figyelmeztetés" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Kikapcsolva" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Fő profil" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Minden (újra)indításkor" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Helyi (EncFS-sel titkosított)" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Ötpercenként" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS-sel titkosított)" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Tízpercenként" +#: common/config.py:237 +msgid "Local" +msgstr "Helyi" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "30 percenként" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Helyi titkosított" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Óránként" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Titkosítás" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Kétóránként" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Négyóránként" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH személyes kulcs" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Hatóránként" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil: „{name}”" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Tizenkétóránként" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "A biztonsági mentés könyvtára nem érvényes." -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Egyedi beállítás (óra)" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Legalább egy könyvtárat ki kell választani a biztonsági mentéshez." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Naponta" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Könyvtár: {path}" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Ez a könyvtár nem szerepelhet a biztonsági mentésben, mivel magának a " +"biztonsági mentési célnak a része." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Ha csatlakozik a meghajtó (udev)" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Hetente" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Havonta" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Nap" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Hét" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Év" - -#: ../../common/config.py:102 -msgid "Hour(s)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"Az „A legrégebbi biztonsági mentés eltávolítása, ha a szabad hely kevesebb " +"mint” értékének ({val_one}) kisebbnek vagy egyenlőnek kell lennie a " +"„Figyelmeztetés, ha a szabad lemezterület ez alá csökken” küszöbszintjével " +"({val_two})." -#: ../../common/config.py:105 -msgid "Month(s)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" +"Állítsa be a beállításokat úgy, hogy a biztonsági mentés eltávolítási " +"korlátja ne legyen magasabb a figyelmeztetési korlátnál." -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " KÍSÉRLETI!" - -#: ../../common/config.py:129 -msgid "Local" -msgstr "Helyi" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Az udev ütemezése nem működik a(z) {mode} móddal" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Nem sikerült az új crontab írása." -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "SSH privát kulcs" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"A cron nem fut, annak ellenére hogy a crontab parancs elérhető. A ütemezett " +"biztonsági mentési feladatok nem fognak futni. Lehet, hogy a cron telepítve " +"van, de nincs engedélyezve. Próbálja meg lefuttatni a „systemctl enable " +"cron„ és „systemctl start cron” parancsokat, vagy kérjen segítséget a " +"jelenleg használt GNU/Linux disztribúció támogatási csatornáin." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Nem sikerült elmenteni a beállításokat" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Nem sikerült betölteni a beállításokat" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "A(z) „{name}” profil már létezik." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Az utolsó profilt nem lehet eltávolítani." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Nem lehet csatolni a(z) „{command}” parancsot" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "A titkosított könyvtár beállítása nem található." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Létrehoz egy új titkosított könyvtárat?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Mégse" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Helyi titkosított" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Adja meg újra az EncFS jelszót a megerősítéshez." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Titkosítás" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "Az EncFS jelszavak nem egyeznek." -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH-val titkosított" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Pillanatkép készítése" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Alapértelmezett" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Nem lehet előkészíteni a(z) „{command}” titkosított útvonalat" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "" +"Nem lehet leválasztani a(z) {mountprocess} folyamatot a(z) {mountpoint} " +"csatolási pontról." -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"A(z) {command} nem található. Telepítse (például ezzel: „{installcommand}”)" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "A(z) {mntpoint} csatolási pont nem üres." -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Adja meg a(z) „{profile}” {mode} profil jelszavát:" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Nem sikerült telepíteni az Udev-szabályt a(z) {profile_id} profilnál. A(z) " +"„{dbus_interface}” DBus-szolgáltatás nem volt elérhető." -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Nem található a(z) „{path}” UUID-ja" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "SIKERTELEN" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Jogosultságok helyreállítása" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Kész" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"A tartalmazási lista következő bejegyezéseinek nincs megfelelő fájlja vagy " +"könyvtára a biztonsági mentés forrásában:" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Biztonsági mentés elhalasztása akkumulátorról való működésnél" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Nem található a biztonsági mentés könyvtára." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Fő profil" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Ha cserélhető meghajtón van, akkor helyezze be." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil: \"%s\"" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "{n} másodperc várakozás." +msgstr[1] "{n} másodperc várakozás." -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "A pillanatkép-könyvtár nem érvényes" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Nem sikerült létrehozni a(z) {snapshot_id} biztonsági mentést." -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Legalább egy könyvtárat ki kell jelölnie a mentéshez" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Legyen türelemmel. Véglegesítés…" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Nem adható hozzá a biztonsági mentés mappája" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Nem lehet létrehozni a könyvtárat." -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Nem adható hozzá a biztonsági mentés almappája" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Beállítófájl mentése…" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "A(z) %s nem könyvtár" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Jogosultságok mentése…" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." msgstr "" +"Található egy befejezetlen {snapshot_id} biztonsági mentés, amely " +"folytatható." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" msgstr "" -"Nem tudok ide írni: %s\n" -"Biztos, hogy van írási jogod?" +"A befejezetlen {snapshot_id} könyvtár eltávolítása a legutóbbi futtatásból" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Nem lehet eltávolítani a könyvtárat" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Biztonsági mentés létrehozása" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Linkek másolása (szimbolikus linkeknél nem csak a hivatkozást)" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Sikeres" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Szakértői beállítások" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Részleges átvitel egy hiba miatt" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "Részleges átvitel az eltűnt forrásfájlok miatt (lásd: „man rsync”)" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"A crontab nem található.\n" -"Biztos, hogy telepítve van a cron?\n" -"Ha nem, akkor ki kell kapcsolni az automatikus biztonsági mentést." +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "Az „rsync” a(z) {exit_code} számú hibakóddal fejeződött be" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Nézze meg a „man rsync” kézikönyvet a további részletekért" -#: ../../common/config.py:1575 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" +"A negatív rsync kilépési kódok szignálszámok, nézze meg a „kill -l” és a " +"„man kill” parancsokat" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Az udev időzítése nem működik ezzel a móddal: %s" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Semmi sem változott, nincs szükség új biztonsági mentésre" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Nem találom a \"%s\" meghajtó UUID-ját" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Nem lehet átnevezni a(z) {new_path} útvonalat erre: {path}." -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" -"Nem tudtam felcsatolni a következőt: '%(command)s':\n" -"\n" -"%(error)s" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Szabályok alkalmazása a régi biztonsági mentések eltávolításához" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "A titkosított mappa konfigurációja nem található" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Megtartási irányelv alkalmazása" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Létrehozzak egy új titkosított mappát?" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Kísérlet a legkisebb szabad hely megtartására" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Mégsem" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Kísérlet legalább {perc} szabad inode megtartására" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Erősítsd meg a jelszót" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Most" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "A jelszavakat nem egyeznek" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Nem lehet csatolni: {sshfs}" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." msgstr "" -"Az encfs az 1.7.2-es és korábbi verzióknál hibás, ha a --reverse opciót " -"használod. Frissítsd az encfs-t" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Pillanatkép készítése" +"Az „ssh-agent” nem található. Győződjön meg arról, hogy telepítve van." -#: ../../common/mount.py:415 -#, python-format +#: common/sshtools.py:489 msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" -"Hash-ütközés történt: hash_id %s. Megnövelem a hash_collision értékét, és " -"újra próbálom." +"Nem sikerült feloldani az SSH személyes kulcsát. Hibás a jelszó vagy a " +"jelszó nem érhető el a cron számára." -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "A távoli útvonal létezik, de nem könyvtár." -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "A távoli útvonal nem írható." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" -"A(z) %(user)s nem tagja a 'fuse' csoportnak.\n" -" Futtasd le a 'sudo adduser %(user)s fuse' parancsot, majd jelentkezz ki és " -"vissza.\n" -"Nézd meg a 'man backintime'-ot további részletekért." +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "A távoli útvonal nem futtatható." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "A %s csatolási pont foglalt" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Nem sikerült létrehozni a távoli útvonalat." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "A csatolási folyamat túl sok időt vett igénybe" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "A(z) {host} távoli kiszolgáló nem támogatja a(z) {command} parancsot" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" -"A(z) '%(profile)s' profil: add meg a jelszót a következőhöz: %(mode)s " +"A parancsok ellenőrzése a(z) {host} kiszolgálón ismeretlen hibát adott " +"vissza" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" msgstr "" -"### Ezt a naplófájlt automatikus keresési mintával dekódolták\n" -"### Ha néhány útvonal dekódolása elmaradt, a következővel tudod kézzel " -"dekódolni:\n" - -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "MEGHIÚSULT" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Engedélyek visszaállítása:" +"A(z) {host} távoli kiszolgáló nem támogatja a rögzített hivatkozásokat" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Kész" - -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" +"A(z) „{pubkey}” nyilvános SSH-kulcs másolása a(z) „{host}” távoli " +"kiszolgálóra." -#: ../../common/snapshots.py:669 +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Adja meg „{user}” felhasználó jelszavát." + +#: common/tools.py:382 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"A pillanatképek könyvtára nem található.\n" -"Ha egy eltávolítható adathordozón van, csatlakoztasd." - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "%s másodperc várakozás." -msgstr[1] "%s másodperc várakozás." - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "A %s pillanatkép készítése meghiúsult!!!" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Véglegesítés" +"A(z) {path} útvonal célfájlrendszere NTFS-re van formázva, amely nem " +"kompatibilis a Unix-stílusú fájlrendszerekkel." -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "A(z) %s mappa nem hozható létre" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "A(z) {path} nem érvényes könyvtár." -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Konfigurációs fájl mentése..." +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "A következő könyvtár létrehozása nem sikerült:" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Hozzáférési jogosultságok mentése..." +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Az írási hozzáférés korlátozott lehet." -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"A(z) {path} útvonal célfájlrendszere FAT-tal van formázva, amely nem " +"támogatja a rögzített hivatkozásokat. Használjon natív GNU/Linux " +"fájlrendszert." -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"A(z) {path} útvonal célfájlrendszere egy SMB csatolású megosztás. Győződjön " +"meg arról, hogy a távoli SMB-kiszolgáló támogatja-e a szimbolikus " +"hivatkozásokat vagy kapcsolja be a „{copyLinks}” lehetőséget az " +"„{expertOptions}” alatt." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "A(z) %s mappa nem távolítható el" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Hivatkozások másolása (szimbolikus hivatkozások megszüntetése)" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Pillanatkép készítése" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Szakértői beállítások" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"A(z) {path} útvonal célfájlrendszere egy sshfs csatolású megosztás. Az sshfs" +" nem támogatja a rögzített hivatkozásokat. Használja inkább az „SSH” módot." -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Nem tudom a(z) %(path)s útvonalat átnevezni erre: %(new_path)s" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "A fájl létrehozása ebben a könyvtárban nem sikerült:" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Okos eltávolítás" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "A Back In Time névjegye" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Régi pillanatképek eltávolítása" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Szerzői jog:" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "A minimálisan megadott szabad hely kialakítása" - -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Megpróbálok legalább %d%% szabad inode-ot tartani" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Szerzők:" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "HIBÁKKAL!" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Fordítók:" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Most" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Úr Balázs , 2024, 2025, 2026." -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Nem tudom felcsatolni: %s" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "A fordítók névsora nem érhető el a jelenlegi nyelvhez." -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" -"Nem tudtam feloldani az ssh privát kulcsát. Hibás jelszó vagy a cron nem éri " -"el a jelszót." +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "ezt a hivatkozást" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" -"A %(user)s@%(host)s nem tudott jelszó nélkül bejelentkeztni. Nézd meg a 'man " -"backintime'-ot további részletekért." +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "Kövesse {thislink} az összes nyelv fordítói névsorának lekéréséhez." -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" -"A(z) %(cipher)s nem sikerült a %(host)s esetében:\n" -"%(err)s" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Projekt webhelye" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "A(z) %s nem található itt: ssh_known_hosts." +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Felhasználói kézikönyv" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -"A távoli útvonal létezik, de nem könyvtár:\n" -" %s" +"Felhasználói kézikönyv megnyitása a böngészőben (helyileg, ha elérhető, " +"egyébként az internetről)" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" -"A távoli útvonal nem írható:\n" -" %s" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Verzió{BOLDEND}: {version}" -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -"A távoli útvonal nem futtatható:\n" -" %s" +"Úgy tűnik, hogy a(z) {app_name} először lett elindítva, mert nem található " +"beállítás hozzá." -#: ../../common/sshtools.py:474 -#, python-format +#: qt/app.py:337 msgid "" -"Couldn't create remote path:\n" -" %s" +"Import an existing configuration from a backup location or another computer?" msgstr "" -"Nem tudom távoli útvonalat elkészíteni:\n" -" %s" +"Importál egy meglévő beállítást (egy biztonsági mentés helyéről vagy egy " +"másik számítógépről)?" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" -"A(z) %s ping sikertelen volt. A kiszolgáló nem működik vagy rossz a cím." +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Ezután nyomja meg a Rendben gombot." -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" -"A(z) %(host)s távoli kiszolgáló nem támogatja a következőt: '%(command)s':\n" -"%(err)s\n" -"Nézd meg a 'man backintime'-ot további részletekért" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Biztonsági mentés létrehozása" -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" -"A parancsok ellenőzése a(z) %(host)s kiszolgálón ismeretlen hibát okozott:\n" -"%(err)s\n" -"Nézd meg a 'man backintime'-ot további részletekért" +"A módosítás ideje és a méret használata a fájlváltoztatások felismeréséhez." -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "A(z) %s kiszolgáló nem támogatja a hardlinkeket." +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Biztonsági mentés létrehozása (ellenőrzőösszeg mód)" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Ellenőrzőösszegek használata a fájlváltoztatások felismeréséhez." -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Biztonsági mentési folyamat szüneteltetése" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Checksum használata hibák felfedezéséhez" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Biztonsági mentési folyamat folytatása" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Biztonsági mentési folyamat leállítása" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Biztonsági mentési lista frissítése" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Biztonsági mentés elnevezése" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Pillanatképek listájának frissítése" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Biztonsági mentés eltávolítása" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Pillanatkép neve" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Biztonsági mentési napló megnyitása" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Pillanatkép eltávolítása" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "A kiválasztott biztonsági mentés naplójának megtekintése." -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "A pillanatképek naplófájljának megtekintése" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Utolsó biztonság mentési napló megnyitása" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Utolsó naplófájl megtekintése" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Az utolsó biztonsági mentés naplójának megtekintése." -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Beállítások" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Profilok kezelése…" -#: ../../qt/app.py:142 +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Felhasználó-visszahívás szerkesztése" + +#: qt/app.py:532 msgid "Shutdown" msgstr "Leállítás" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "Állítsd le a rendszert, ha elkészült a pillanatkép." +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "A rendszer leállítása a biztonsági mentés befejezése után." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Nyelv beállítása…" + +#: qt/app.py:540 msgid "Exit" msgstr "Kilépés" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Súgó" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "kézikönyvoldal: Back In Time" + +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Megjeleníti a Back In Time (backintime) kézikönyvoldalát" -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "kézikönyvoldal: Profilok beállítófájlja" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" +"Megjeleníti a profilok beállítófájljával kapcsolatos kézikönyvoldalt " +"(backintime-config)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Honlap" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "A Back In Time webhelyének megnyitása a böngészőben" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" +msgstr "Változásnapló" + +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" +"Változásnapló megnyitása (helyileg, ha elérhető, egyébként az internetről)" -#: ../../qt/app.py:167 +#: qt/app.py:573 msgid "FAQ" msgstr "GYIK" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Gyakran ismételt kérdések (GYIK) megnyitása a böngészőben" + +#: qt/app.py:577 msgid "Ask a question" -msgstr "Kérdezz" +msgstr "Kérdés feltevése" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Hiba jelentése" -#: ../../qt/app.py:174 ../../qt/app.py:1439 +#: qt/app.py:584 +msgid "Translation" +msgstr "Fordítás" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "" +"Ismét megjeleníti a fordításban való részvétellel kapcsolatos üzenetet." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Titkosítási átmenet (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Ismét megjeleníti az EncFS eltávolításával kapcsolatos üzenetet." + +#: qt/app.py:599 msgid "About" msgstr "Névjegy" -#: ../../qt/app.py:204 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Helyreállítás" + +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "A kijelölt fájlok vagy könyvtárak helyreállítása az eredeti helyre." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Helyreállítás ide…" + +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "A kijelölt fájlok vagy könyvtárak helyreállítása új helyre." + +#: qt/app.py:615 +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "" +"A jelenleg megjelenített könyvtár és annak teljes tartalmának helyreállítása" +" az eredeti helyre." + +#: qt/app.py:621 +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "" +"A jelenleg megjelenített könyvtár és annak teljes tartalmának helyreállítása" +" új helyre." + +#: qt/app.py:624 msgid "Up" msgstr "Fel" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" msgstr "Rejtett fájlok megjelenítése" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Visszaállítás" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Biztonsági mentések összehasonlítása…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Kiadásra jelölt" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Visszaállítás ide..." +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Ismét megjeleníti az ezzel a kiadásra jelölttel kapcsolatos üzenetet." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Biztonsági mentés" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Helyreállítás" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Súgó" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Pillanatképek" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Rendszertálca ikon" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automatikus" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:784 +msgid "Light icon" +msgstr "Világos ikon" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Billentyűparancsok" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Sötét ikon" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Csak ikonok" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:811 +msgid "Text only" +msgstr "Csak szöveg" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Szöveg az ikonok alatt" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Szöveg az ikon mellett" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Ez a könyvtár nem létezik a jelenleg\n" +"kiválasztott biztonsági mentésben." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Nem található a pillanatképek könyvtára.\n" -"Ha egy eltávolítható lemezen van, csatlakoztasd és nyomd meg az OK-t" +"Ez a könyvtár nem létezik a jelenleg\n" +"kiválasztott biztonsági mentésben." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" -"Ha bezárod ezt az ablakot, a Back In Time nem tudja lekapcsolni a rendszert " -"a biztonsági mentés elvégzése után.\n" -"Biztos bezárod?" +"Ha ezt az ablak bezárásra kerül, akkor a Back In Time nem lesz képes " +"leállítani a rendszert a biztonsági mentés befejezése után." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "Dolgozik:" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Mindenképp bezárja az ablakot?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" -msgstr "Kész, nincs szükség biztonsági mentés készítésére" +msgstr "Kész, nincs szükség biztonsági mentésre" + +#: qt/app.py:1260 +msgid "Working:" +msgstr "Munkavégzés:" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Hiba:" +#: qt/app.py:1267 +msgid "Working" +msgstr "Munkavégzés" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Hiba" + +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "Elküldve:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "" +msgstr "Sebesség:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Globális" +msgstr "Becsült hátralévő idő:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Root" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Biztonsági mentés:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Home" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "{path} helyreállítása" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Biztonsági mentés mappái" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "{path} helyreállítása ide…" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Biztos, hogy el akar távolítani a következő pillanatképet:\n" -"%s?" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Üdvözöljük!\n" +"Ön már többször használta a Back In Time programot {language} nyelven.\n" +"A Back In Time telepített verziójának {language} nyelvre történő fordítása {perc} készültségű. Függetlenül attól, hogy milyen szintű műszaki ismeretekkel rendelkezik, hozzájárulhat a fordításhoz, és így magához a Back In Time-hoz is.\n" +"Látogasson el a {translation_platform_url} oldalra, ha szeretne közreműködni. A további segítségért és kérdésekért látogasson el a {back_in_time_project_website} weboldalra.\n" +"Elnézést kérünk a zavarásért. Ez az üzenet nem fog többé megjelenni. Ez a párbeszédablak később is bármikor elérhető a súgó menüben.\n" +"A Back In Time csapata" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "fordítási platform" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Webhely" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Az Ön fordítása" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "A mastodoni födiverzumban: {link_and_label}." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "E-mail küldése: {link_and_label}." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Levelezőlista: {link_and_label}." -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} a projekt webhelyén." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Nyisson hibajegyet" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." msgstr "" +"Alternatívaként egy másik, Ön által választott csatornát is használhat." -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"A Back In Time ezen verziója kiadásra jelölt, és elsősorban a következő hivatalos kiadásra való felkészülés során végzett stabilitástesztekre szolgál.\n" +"Nem gyűjt felhasználói adatokat vagy telemetriát. A Back In Time csapatát azonban nagyon érdekli, hogy a kiadásra jelölt verzió használatban van-e, és érdemes-e folytatni az ilyen kiadás előtti verziók biztosítását.\n" +"Ezért a csapat rövid visszajelzést kér arról, hogy tesztelte-e ezt a verziót, még akkor is, ha nem találkozott semmilyen problémával. Már egy gyors, néhány perces próbahasználat is sokat segítene nekünk.\n" +"A következő kapcsolatfelvételi lehetőségek állnak rendelkezésre:\n" +"{contact_list}\n" +"Ebben a verzióban ez az üzenet nem jelenik meg újra, de bármikor elérhető a súgó menüből.\n" +"Köszönjük a támogatását és azt, hogy segít nekünk a Back In Time fejlesztésében!\n" +"Az Ön Back In Time csapata" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Csak {free} szabad terület érhető el a célon, amely a beállított {threshold}" +" küszöbszint alatt van." -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Folytatja a biztonsági mentést?" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" +"A(z) {path} útvonalon lévő összes újabb fájl el lesz távolítva. Folytatja?" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Az eredeti könyvtárban lévő összes újabb fájl el lesz távolítva. Folytatja?" -#: ../../qt/app.py:1105 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Figyelmeztetés:{BOLDEND} a fájlrendszer gyökerében lévő fájlok törlése" +" tönkreteheti az egész rendszert." + +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Biztonsági mentés neve" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "A lemez jelenlegi tartalmának megtekintése" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Eltávolítja ezt a biztonsági mentést?" +msgstr[1] "Eltávolítja ezeket a biztonsági mentéseket?" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Pillanatkép: %s" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "" +"A nyelvi beállítások csak a Back In Time újraindítása után lépnek érvénybe." -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "A %s időpontban észült pillanatkép megtekintése" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Verziózott biztonsági mentések (rendszergazda)" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "A(z) '%s' visszaállítása" +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" +msgstr "" +"Felhasználóbarát grafikus felület a verziózott biztonsági mentésekhez, ami " +"csökkenti a lemezhasználatot (rendszergazdai mód)" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "A(z) '%s' visszaállítása ide..." +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Verziózott biztonsági mentések" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Felhasználóbarát grafikus felület a verziózott biztonsági mentésekhez, ami " +"csökkenti a lemezhasználatot" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Kérdés" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Biztonsági mentés másolatok létrehozása {suffix} végződéssel a helyi elemek " +"felülírása vagy eltávolítása előtt." -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"A helyreállítás előtt a fájlok újabb verziói átnevezésre kerülnek a " +"hozzáfűzött {suffix} értékkel. Ezek a fájlok a következő paranccsal " +"távolíthatók el:" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Csak azon elemek helyreállítása, amelyek nem léteznek, vagy újabbak a " +"célhelyen lévőknél. Az „{rsync_example}” kapcsoló használata." + +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Újabb elemek eltávolítása az eredeti könyvtárból." -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"A kijelölt fájlok vagy könyvtárak helyreállítása az eredeti célhelyre, " +"valamint a biztonsági mentésben nem szereplő fájlok vagy könyvtárak törlése." +" Legyen rendkívül óvatos, mert ez törölni fogja azokat a fájlokat és " +"könyvtárakat, amelyek a biztonsági mentés létrehozásakor ki voltak zárva." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Valóban helyreállítja ezt az elemet az új könyvtárba?" +msgstr[1] "Valóban helyreállítja ezeket az elemeket az új könyvtárba?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Valóban helyreállítja ezt az elemet?" +msgstr[1] "Valóban helyreállítja ezeket az elemeket?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Felhasználó-visszahívás: „{filename}”" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"A felhasználó-visszahívási parancsfájlnak parancsértelmezőt megadó sort kell" +" tartalmaznia az első sorban (például {example})." + +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Rejtett fájlok és könyvtárak megjelenítése vagy elrejtése (Ctrl+H)" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Hozzáadás a felvettekhez" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Hozzáadás a kizártakhoz" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Válasszon a gyakran használt elemek közül a kizárási listához való " +"hozzáadáshoz." +msgstr[1] "" +"Válasszon a gyakran használt elemek közül a kizárási listához való " +"hozzáadáshoz." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Válasszon a gyakran használt elemek közül a kizárási listához való " +"hozzáadáshoz." +msgstr[1] "" +"Válasszon a gyakran használt elemek közül a kizárási listához való " +"hozzáadáshoz." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Nyelv beállítása" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Lefordítva: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Rendszer alapértelmezettje" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Az operációs rendszer nyelvének használata." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Biztonsági mentési napló nézet" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Utolsó napló megtekintése" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Profil:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Biztonsági mentések:" + +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Szűrő:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] hiba, [I] információ, [C] változtatás" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "útvonalak visszafejtése" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" -msgstr "Mindegyik" +msgstr "Összes" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Változtatások" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Hibák" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Változások" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Információk" +msgstr[1] "Információk" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Információk" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync átviteli hibák (kísérleti)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] hiba, [I] információ, [C] változtat" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Profilok kezelése" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "dekódolás útvonalai" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Szerkesztés" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Hozzáadás" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Eltávolítás" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "Á<alános" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Felvétel" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Kizárás" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Eltávolítás és megtartás" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Dolgozom..." +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Beállítások" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Ma" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "S&zakértői beállítások" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Tegnap" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Beállítások helyreállítása" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Ezen a héten" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Új profil" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Múlt héten" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Profil átnevezése" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Törli a(z) „{name}” profilt?" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Szimbolikus hivatkozások másolása fájlokként" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Szimbolikus hivatkozások valódi fájlokként vagy könyvtárakként való másolása" +" a biztonsági mentésbe. Válassza ki, hogy az összes hivatkozást vagy csak a " +"forráson kívülre mutatókat kell másolni." -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Szerkesztés" - -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Ez a beállítás növelheti a biztonsági mentés méretét." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Alapértelmezetten le van tiltva." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Az összes szimbolikus hivatkozás azokkal a valódi fájlokkal vagy " +"könyvtárakkal kerül helyettesítésre, amelyekre mutatnak. Ez növeli a " +"biztonsági mentés méretét, és ugyanazokat a fájlokat többször is tárolhatja." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Általános" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Az „rsync --copy-links” kapcsolót használja." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Mód:" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Csak külső" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"Csak a biztonsági mentés forrásán kívülre mutató hivatkozások lesznek " +"fájlokként másolva. Ez növeli a biztonsági mentés méretét, és ugyanazokat a " +"fájlokat többször is tárolhatja." + +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Az „rsync --copy-unsafe-links” kapcsolót használja." + +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Szerkesztő és irodai programcsomag ideiglenes fájljai" + +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacs biztonsági mentési fájlok" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Hova mentse a pillanatképeket" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs automatikusan mentett fájljai" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Vim cserehelyfájlok" + +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Office ideiglenes fájljai" + +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice és egyéb OpenDocument-szerkesztők zárolási fájljai" + +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Bélyegképek és ideiglenes képek" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" +"Bélyegkép-gyorsítótár GNU/Linux és egyéb Unix-szerű operációs rendszereken" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "Az SSH beállításai" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Bélyegkép-adatbázis Windows rendszeren" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Kiszolgáló:" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Metaadatkönyvtár MacOS rendszeren" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Port:" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Alkalmazásspecifikus zárolások" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Felhasználó:" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discord-alkalmazás zárolási fájlja" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Elérési út:" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord-munkamenet zárolási fájlja" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "Cipher:" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Mozilla Firefox és Thunderbird zárolási fájlja" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Privát kulcs" +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Gyorsítótárak és ideiglenes könyvtárak" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "A felhasználó alkalmazás-gyorsítótára" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "A rendszer ideiglenes könyvtára" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Jelszó" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Csomaggyorsítótár Debian(-alapú) GNU/Linux disztribúcióknál" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Jelszó mentése a kulcstartóba" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Flatpak alkalmazás- és futtatókörnyezet-tárolók" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" -"Jelszó mentése a Cron számára (biztonsági figyelmeztetés: a root látja a " -"jelszót)" +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "A rendszer futtatókörnyezet-könyvtárai" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Szakértői" +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Rendszermag- és folyamatinformációk" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Eszköz- és egyéb hardverinformációk (sysfs csatoló)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Eszközcsomópontok" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Futtatókörnyezet rendszerfájljai" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Egyéb nem tartós" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Jelenleg csatolt fájlrendszerek listája" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "A rendszer cserehelyfájlja (virtuális memória)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "GNOME virtuális fájlrendszer csatolási pontja" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Helyreállított fájlrendszerobjektumok" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Egyebek" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Metaadatkönyvtár Microsoft Windows rendszeren" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "A felhasználó kukája" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "A rendszer biztonsági mentési fájljai" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Javaslatok kizárása" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" +"A gyakran használt elemek kiválasztása a biztonsági mentés kizárásaihoz való" +" hozzáadáshoz." + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Alapértelmezett" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Visszaállítás az előre meghatározott kiválasztásra" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" -msgstr "Ütemterv" +msgstr "Ütemezés" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Nap:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" msgstr "Hét napja:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Óra:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Idő:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "Óra:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "óra után" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Perc:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"A Back In Time futtatása, amint a meghajtó csatlakoztatásra kerül (X naponta" +" csak egyszer). Meg fog jelenni a sudo jelszó kérése." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"A Back In Time futtatása ismételten. Ez akkor hasznos, ha a számítógép nincs" +" rendszeresen bekapcsolva." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" +msgstr "Minden:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Hibakeresési üzenetek naplózásának engedélyezése" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Hibakeresés szintű üzeneteket ír a rendszernaplóba a „--debug” kapcsolón " +"keresztül." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -"Futtassa a Back In Time-ot, amint csatolod a meghajtót (X naponta egyszer).\n" -"Be fogja kérni a sudo jelszót." +"Vigyázat: ezt csak átmenetileg használja diagnosztikára, mivel nagy " +"mennyiségű kimenetet állít elő." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Hozzáadás" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Letiltva" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Minden indításkor vagy újraindításkor" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Percenként" +msgstr[1] "{n} percenként" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Óránként" +msgstr[1] "{n} óránként" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Felveendő fájlok és mappák" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Egyéni órák" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Fájl hozzáadása" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Naponta" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Mappa hozzáadása" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Ismételten (anacron)" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Kihagyás" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Ha meghajtó kerül csatlakoztatásra (udev)" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" -"Figyelem: a helyettesítő karaktreket ('foo*', '[fF]oo', 'fo?') az " -"'SSH encrypted' mód nem veszi figyelembe.\n" -"Csak az elválasztó csillag megengedett ('foo/*', 'foo/**/bar')" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Hetente" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Kihagyandó minták, fájlok és mappák" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Havonta" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Erősen ajánlott:" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Évente" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Óra" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Nap" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Hét" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Hónap" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Az egyéni órák csak az órák vesszővel elválasztott listája lehet (például " +"8,12,18,23) vagy */3 a háromóránkénti rendszeres biztonsági mentésekhez." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Automatikus eltávolítás" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Pilih berkas kunci privat yang sudah ada dari tempat lain." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Buat kunci SSH baru tanpa kata sandi." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Path lengkap: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Kunci privat:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Gunakan konfigurasi SSH sistem" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Membiarkan berkas kunci tidak dipilih. Koneksi SSH akan bergantung pada " +"konfigurasi klien yang ada di sistem (misalnya, ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Proksi SSH" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Host:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Pengguna:" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Menyambung ke host target melalui proksi ini (juga dikenal sebagai jump " +"host). Lihat \"-J\" dalam dokumentasi perintah \"ssh\" atau \"ProxyJump\" " +"dalam halaman man \"ssh_config\" untuk rinciannya." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" +"{BOLD}Info{ENDBOLD}: Dalam mode 'SSH terenkripsi', hanya asterisk tunggal " +"atau ganda yang berfungsi (mis. {example2}). Tipe wildcard dan pola lain " +"akan diabaikan (mis. {example1}). Nama berkas itu tidak dapat ditebak dalam " +"mode ini karena enkripsi oleh EncFS." -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Kecualikan pola, berkas, atau direktori" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Tambahkan pola" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Tambahkan berkas" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Tambahkan direktori" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Saran" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" +"Pilih dari butir yang umum digunakan untuk ditambahkan ke daftar " +"pengecualian." + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Abaikan berkas yang lebih dari:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Abaikan berkas yang lebih dari {size_unit}." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Dengan menonaktifkan 'Mode rsync penuh', pengaturan ini hanya memengaruhi " +"berkas yang baru dibuat, karena rsync memperlakukannya sebagai opsi transfer" +" dan bukan aturan pengecualian. Akibatnya, berkas besar yang telah " +"dicadangkan akan tetap ada dalam cadangan meskipun dimodifikasi." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Abaikan pola" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Masukkan pola pengecualian:" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Untuk bantuan, lihat halaman manual rsync bagian {link}." -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Buka halaman manual rsync" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Tidak ada" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Kecualikan berkas" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Perubahan dan Kesalahan" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Kecualikan direktori" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" +"Dinonaktifkan karena pola ini tidak berfungsi dalam mode 'SSH terenkripsi'." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Opsi-opsi ini untuk konfigurasi lanjutan. Ubah hanya jika Anda benar-benar " +"memahami dampaknya." -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Jalankan 'rsync' dengan '{cmd}':" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "sebagai cron job" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" +msgstr "pada host jarak jauh" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "saat melakukan pencadangan manual" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Mohon pasang 'nocache' untuk mengaktifkan opsi ini." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" - -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "pada mesin lokal" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Arahkan ulang stdout ke /dev/null dalam cronjobs." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron akan secara otomatis mengirim suatu surel dengan keluaran cronjob yang " +"dilampirkan bila suatu MTA terpasang." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Arahkan ulang stderr ke /dev/null dalam cronjobs." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron akan secara otomatis mengirim suatu email dengan kesalahan cronjob yang" +" dilampirkan bila suatu MTA terpasang." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/detik" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Batasi penggunaan bandwidth rsync:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "" +msgstr "Pertahankan ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "" +msgstr "Pertahankan atribut tambahan (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Batasi ke satu sistem berkas" -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Opsi harus diapit tanda kutip mis.: {example}." -#: ../../qt/settingsdialog.py:839 -msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "Tempelkan opsi tambahan ke rsync" -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefiks yang dijalankan sebelum setiap perintah pada host jarak jauh." -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Variabel perlu di-escape dengan \\$FOO. Ini tidak menyentuh rsync. Jadi " +"untuk menambah awalan bagi rsync gunakan \"{example_value}\" dengan " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 +#: qt/manageprofiles/tab_expert_options.py:293 msgid "default" -msgstr "" +msgstr "baku" + +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Tambahkan prefiks ke perintah-perintah SSH" -#: ../../qt/settingsdialog.py:870 +#: qt/manageprofiles/tab_expert_options.py:308 msgid "Check if remote host is online" -msgstr "" +msgstr "Periksa apakah host jarak jauh sedang daring" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Peringatan: jika dimatikan dan host jarak jauh tidak tersedia, ini dapat " +"menyebabkan kesalahan yang aneh." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." msgstr "" +"Periksa apakah host jarak jauh mendukung semua perintah yang diperlukan." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Peringatan: jika dimatikan dan host jarak jauh tidak mendukung semua " +"perintah yang dibutuhkan, ini dapat menyebabkan beberapa kesalahan aneh." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(baku: {})" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "dinonaktifkan" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "difungsikan" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Mode:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Di mana menyimpan cadangan" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Pengaturan SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Path:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Berkas kunci:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Kata Sandi" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Simpan Kata Sandi ke Ring Kunci" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Singgahkan Kata Sandi untuk Cron (Masalah keamanan: root dapat membaca kata " +"sandi)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Tingkat Lanjut" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Path cadangan lengkap:" + +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" +"Penjadwalan dinonaktifkan karena tidak ditemukan instalasi cron. Silakan " +"pasang cron untuk mengaktifkan pencadangan terjadwal." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Profil baru" - -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Ubah nama profil" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Path tujuan pencadangan tidak boleh kosong." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Apakah anda yakin untuk menghapus profile \"%s\" ?" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Kata sandi enkripsi tidak boleh kosong." -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -"Custom hour hanya dapat menjadi koma pemisah dalam daftar jam (misal " -"8,12,18,23) atau */3 untuk backup periodik setiap 3 jam" +"Terjadi kesalahan saat mencoba masuk ke host jarak jauh. Pesan kesalahan " +"berikut dikembalikan:" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Untuk mengaktifkan login tanpa kata sandi, kunci SSH publik dapat disalin ke" +" host jarak jauh." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Lanjutkan dengan menyalin kunci SSH?" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Kunci SSH publik tidak dapat disalin. Hal ini mungkin disebabkan oleh " +"masalah koneksi atau izin." + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Keaslian host \"{host}\" tidak dapat diyakinkan." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "Sidik jari kunci {keytype} adalah:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "Silakan verifikasi sidik jari ini. Tambahkan ke berkas \"known_hosts\"?" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Benar-benar mengubah direktori cadangan?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Tujuan pencadangan yang dipilih tidak kosong." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Itu harus kosong untuk menggunakan enkripsi." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Berkas tidak valid: Bukan kunci SSH privat" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"Berkas yang dipilih ({path}) adalah kunci SSH publik. Silakan pilih berkas " +"kunci privat yang sesuai sebagai pengganti (tanpa \".pub\")." -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" +"Berkas {path} sudah ada. Tidak dapat membuat kunci SSH baru dengan nama " +"tersebut." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Gagal membuat kunci SSH baru dalam {path}." + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Sertakan berkas dan direktori" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"\"{path}\" adalah symlink. Target yang ditaut tidak akan dicadangkan sampai " +"target tersebut juga disertakan." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Sertakan target symlink sebagai gantinya?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Sertakan berkas" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Sertakan direktori" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Fungsikan pemberitahuan" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Nonaktifkan pencadangan saat menggunakan baterai" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Status daya tidak tersedia dari sistem" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Jalankan hanya satu pencadangan dalam satu waktu" + +#: qt/manageprofiles/tab_options.py:56 +msgid "" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Pencadangan lainnya akan diblokir hingga pencadangan saat ini selesai. Ini " +"adalah pengaturan global, yang berarti akan memengaruhi semua profil " +"pengguna ini. Namun, pengaturan ini juga harus diaktifkan untuk semua " +"pengguna lain." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Backup menggantikan berkas-berkas saat pemulihan" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" msgstr "" +"Lanjutkan meskipun terjadi kesalahan (simpan cadangan yang belum lengkap)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Gunakan checksum untuk mendeteksi perubahan" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Buat cadangan baru, baik ada perubahan atau tidak." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Beri peringatan jika ruang disk kosong kurang dari" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Menampilkan peringatan ketika ruang kosong pada disk tujuan pencadangan " +"kurang dari nilai yang ditentukan." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Jika kebijakan Hapus & Retensi diaktifkan dan cadangan lama dihapus " +"berdasarkan ruang kosong yang tersedia, nilai ini tidak boleh lebih rendah " +"dari nilai yang ditetapkan dalam kebijakan tersebut." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Level Log:" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nihil" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "manual pengguna" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Aturan-aturan berikut diproses dari atas ke bawah. Aturan-aturan selanjutnya" +" akan menggantikan aturan-aturan sebelumnya. Lihat {manual_link} untuk " +"detail dan contohnya." + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Buka manual pengguna dalam peramban." -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Apakah anda yakin ingin mengubah folder snapshot?" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Simpan cadangan terbaru." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Cadangan data terbaru selalu disimpan dalam segala keadaan." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Perilaku itu tidak bisa diubah." + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Simpan cadangan yang dinamai." + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Cadangan yang telah diberi nama, selain stempel waktu biasa, akan disimpan " +"dalam semua keadaan dan tidak akan dihapus." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Tahun" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Hapus cadangan yang lebih lama dari" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Hari penuh. Hari kini diabaikan." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Minggu kalender dengan Senin sebagai hari pertama. Minggu kini diabaikan." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Periode 12 bulan. Bulan kini diabaikan." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Kebijakan retensi" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Jalankan di latar belakang pada host jarak jauh." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"Kebijakan retensi akan dieksekusi langsung pada mesin jarak jauh, bukan " +"secara lokal. Perintah \"bash\", \"screen\", dan \"flock\" harus dipasang " +"dan tersedia pada mesin jarak jauh tersebut." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." msgstr "" +"Jika dipilih, Back In Time akan terlebih dahulu menguji mesin jarak jauh." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Hari-hari dihitung mulai dari hari ini." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Simpan semua cadangan untuk yang terakhir" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "hari terakhir." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Simpan cadangan terakhir setiap hari untuk yang terakhir" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Minggu-minggu dihitung mulai dari minggu kini yang sedang berjalan. Suatu " +"minggu mulai pada hari Senin." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Simpan cadangan terakhir setiap minggu untuk yang terakhir" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "minggu terakhir." -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:357 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The months are counted as calendar months starting with the current month." +msgstr "Bulan-bulan dihitung sebagai bulan kalender mulai dari bulan kini." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Simpan cadangan terakhir setiap bulan untuk yang terakhir" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "bulan terakhir." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Tahun-tahun dihitung sebagai tahun kalender mulai dari tahun kini." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Simpan cadangan terakhir setiap tahun untuk" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "semua tahun." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "... ruang kosong kurang dari" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "... inode bebas kurang dari" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Hapus cadangan terlama jika …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Pintasan" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Tempat" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Sistem Berkas" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Cadangkan direktori" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: {profile_name} (oleh pengguna \"{desktop_user}\")" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Lihat Catatan Log Terakhir" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Mulai {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Sedang Bekerja…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "halaman man: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Impor konfigurasi" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Impor" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Mencari…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Pilih direktori cadangan tempat asal berkas konfigurasi mesti diimpor. Path-" +"nya mungkin seperti ini: {samplePath}" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Bila direktori terletak pada drive eksternal atau jarak jauh, itu mesti " +"dikait secara manual sebelumnya." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Pindai lagi" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Tampilkan direktori tersembunyi" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Tampilkan/sembunyikan direktori tersembunyi (Ctrl+H)" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Pembuatan berkas gagal di direktori ini:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Pencarian selesai." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Tampilkan log lengkap" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Hitung Mundur ke Mematikan" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Proses pencadangan telah selesai." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Batalkan Mematikan" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Matikan Sekarang" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Sistem akan dimatikan dalam {n} detik." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Opsi tentang membandingkan cadangan" + +#: qt/snapshotsdialog.py:68 msgid "Command:" -msgstr "" +msgstr "Perintah:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" -msgstr "" +msgstr "Parameter:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "" +msgstr "Gunakan %1 dan %2 untuk parameter path" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Harap atur suatu perintah diff atau tekan Batal." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"Perintah \"{cmd}\" tidak bisa ditemukan pada sistem ini. Harap coba yang " +"lain atau tekan Batal." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Tidak ada parameter yang diatur untuk perintah diff. Memakai nilai baku " +"\"{params}\"." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Cadangan" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Hanya cadangan yang berbeda" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Cantumkan hanya cadangan yang sama dengan:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Pemeriksaan mendalam (lebih akurat, tetapi lambat)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Hapus" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Pilih Semua" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Beda" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Bandingkan" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" -msgstr "" +msgstr "Pergi Ke" + +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opsi" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." msgstr "" -"Anda tidak dapat membandingkan sebuah snapshot dengan dirinya sendiri" +"Tidak mungkin membandingkan cadangan dengan dirinya sendiri, karena " +"perbandingan tersebut akan redundan." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Benar-benar menghapus {file_or_dir} di cadangan {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Benar-benar menghapus {file_or_dir} di {count} cadangan?" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Perintah tidak ditemukan: %s" +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "PERINGATAN: Ini tidak dapat dicabut." -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Kecualikan {path} dari pencadangan di masa mendatang?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Mode root" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -"Apakah anda yakin ingin menghapus \"%(file)s\" dalam snapshot " -"\"%(snapshot_id)s?\n" +"Back In Time saat ini berjalan dengan hak akses root (akses sistem penuh)" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Hari ini" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Kemarin" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Minggu ini" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Minggu lalu" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -"Apakah anda yakin ingin menghapus \"%(file)s\" dalam %(count)d snapshot?\n" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "PERINGATAN: Ini tidak bisa dibatalkan!" +#: qt/timeline.py:95 +msgid "Last month" +msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Pemeriksaan terakhir {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Ini BUKAN cadangan, melainkan tampilan langsung dari berkas lokal." diff --git a/common/po/is.po b/common/po/is.po index 0ac628351..3f9f732c2 100644 --- a/common/po/is.po +++ b/common/po/is.po @@ -1,1670 +1,2539 @@ -# translation of 20110902-common_po_common-is.po to Icelandic -# Icelandic translation for backintime -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the backintime package. +# SPDX-FileCopyrightText: © 2008 Back In Time Team # -# FIRST AUTHOR , 2011. -# Sveinn í Felli , 2011, 2015, 2017. +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: 20110902-common_po_common-is\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2017-11-09 12:01+0000\n" -"Last-Translator: Sveinn í Felli \n" -"Language-Team: Icelandic \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-01-03 03:42+0000\n" +"Last-Translator: sveinki \n" +"Language-Team: Icelandic \n" +"Language: is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" -"Language: is\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Mistókst að vista stillingaskrá: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Mistókst að hlaða inn stillingaskrá: %s" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Sniðið \"%s\" er þegar til staðar !" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Þú getur ekki fjarlægt síðasta snið !" +"X-Generator: Weblate 5.9.2\n" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Óvirkt" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Við hverja ræsingu/endurræsingu" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Á 5 mínútna fresti" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Á 10 mínútna fresti" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Aðvörun" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Á 30 mínútna fresti" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Aðalsnið" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Á klukkustundar fresti" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Staðvært (EncFS-dulritað)" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Á 2 klukkustunda fresti" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS-dulritað)" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Á 4 klukkustunda fresti" +#: common/config.py:237 +msgid "Local" +msgstr "Staðvært" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Á 6 klukkustunda fresti" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Staðvært dulritað" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Á 12 klukkustunda fresti" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Dulritun" -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Sérsniðnar klukkustundir" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Daglega" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH-einkalykill" + +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Snið: \"{name}\"" + +#: common/config.py:301 +#, fuzzy +msgid "Backup directory is not valid." +msgstr "Mappa fyrir skyndiafrit er ekki gild." + +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Að minnsta kosti ein mappa verður að vera undir öryggisafrit." + +#: common/config.py:331 common/config.py:346 +#, fuzzy, python-brace-format +msgid "Directory: {path}" +msgstr "Endurheimta {path}" + +#: common/config.py:332 common/config.py:347 +#, fuzzy +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "Mappan getur ekki verið innifalin í öryggisafritinu." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Endurtekið (anacron)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Þegar drif er tengt (udev)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Vikulega" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Áætlað udev virkar ekki með hamnum {mode}" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Mánaðarlega" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Mistókst að skrifa nýtt crontab." -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "dag(a)" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "viku(r)" +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Mistókst að vista stillingaskrá" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "ár" +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Mistókst að hlaða inn stillingaskrá" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "klukkustund(ir)" +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Sniðið \"{name}\" er þegar til staðar." -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "mánuð(ir)" +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Ekki er hægt að fjarlægja síðasta sniðið." -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " Á tilraunastigi!" +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Get ekki tengt '{command}'" -#: ../../common/config.py:129 -msgid "Local" -msgstr "Staðvært" +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Grunnstillingar fyrir dulritaða möppu fundust ekki." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Búa til nýja dulritaða möppu?" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "SSH einkalykill" +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Hætta við" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Staðvært dulritað" +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "Settu inn lykilorð fyrir \"{user}\"." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Dulritun" +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "Lykilorðin samsvara ekki." -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH dulritað" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Taka skyndiafrit" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Sjálfgefið" +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Get ekki tengt '{command}'" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Get ekki aftengt {mountprocess} úr {mountpoint}." -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"{command} fannst ekki. Endilega settu það upp (t.d. með " +"\"{installcommand}\")" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Tengipunktur {mntpoint} er ekki laus." -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Settu inn lykilorð fyrir {mode}-notkunarsniðið \"{profile}\":" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/schedule.py:238 +#, fuzzy, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Gat ekki installað Udev reglu fyrir prófíl {profile_id}. DBus þjónusta " +"'{dbus_interface}' var ekki í boði" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Gat ekki fundið UUID fyrir {path}" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "MISTÓKST" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Endurheimta heimildir" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Búið" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Fresta afritun þegar rafhlöður eru í notkun" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:931 qt/app.py:377 +#, fuzzy +msgid "Can't find backup directory." +msgstr "Finn ekki möppu fyrir skyndiafrit." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Aðalsnið" +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "" +"Ef hún er á útskiptanlegu drifi, skaltu tengja það og ýta síðan á 'Í lagi'." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Snið: \"%s\"" +#: common/snapshots.py:938 +#, fuzzy, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Bið %s sekúndu." +msgstr[1] "Bið %s sekúndur." -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Mappa fyrir skyndiafrit er ekki gild !" +#: common/snapshots.py:1005 +#, fuzzy, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Mistókst að taka skyndiafritið {snapshot_id}." -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Þú verður að velja a.m.k. eina möppu til að öryggisafrita !" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Sýndu þolinmæði. Er að klára.…" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Þú mátt ekki hafa með möppuna sem öryggisafritin eiga að fara í !" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Get ekki búið til möppu." -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Þú mátt ekki hafa með undirmöppu öryggisafritamöppunnar!" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Vista stillingaskrá…" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s er ekki mappa !" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Vista aðgangsheimildir…" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" +#: common/snapshots.py:1377 +#, fuzzy, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." msgstr "" +"Fann leifar af skyndiafritinu {snapshot_id} sem hægt er að halda áfram með." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Get ekki skrifað í: %s\n" -"Ertu viss um að þú hafir skrifaðgang þarna ?" +#: common/snapshots.py:1401 +#, fuzzy, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Fjarlægi leifar af möppunni {snapshot_id} frá síðustu keyrslu" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Get ekki fjarlægt möppu" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +#: common/snapshots.py:1466 +msgid "Creating backup" msgstr "" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Afrita tengla (afbyggja tákntengi - dereference symbolic links)" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Tókst" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Ítarlegri valkostir" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Einungis hluti skráaflutnings vegna villu" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" +"Einungis hluti skráaflutnings vegna horfinna upprunaskráa (skoðaðu 'man " +"rsync')" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Finn ekki crontab.\n" -"Ertu viss um að cron sé uppsett ?\n" -"Ef ekki, ættirðu að gera alla sjálfvirka öryggisafritun óvirka." +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' hætti með stöðvunarkóðanum {exit_code}" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "Mistókst að skrifa nýtt crontab." +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Skoðaðu 'man rsync' til að sjá frekari upplýsingar" -#: ../../common/config.py:1575 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Áætlað udev virkar ekki með hamnum %s" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Gat ekki fundið UUID fyrir \"%s\"" +#: common/snapshots.py:1566 +#, fuzzy +msgid "Nothing changed, no new backup necessary" +msgstr "Engar breytingar, engin þörf fyrir nýtt skyndiafrit" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" -"Get ekki tengt '%(command)s':\n" -"\n" -"%(error)s" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Gat ekki endurnefnt {new_path} sem {path}." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "Fjarlægja gömul skyndiafrit" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" +#: common/snapshots.py:2031 +msgid "Applying retention policy" msgstr "" -"\n" -"Búa til nýja dulkóðaða möppu?" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Hætta við" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Reyna að halda í lágmarksmagn af lausu plássi" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Staðfestu aðgangsorð" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Reyna að halda a.m.k. {perc} af lausum i-hnútum (inodes)" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Lykilorðin stemma ekki" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Núna" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Get ekki tengt {sshfs}" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Taka skyndiafrit" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent fannst ekki. Gakktu úr skugga um að það sé uppsett." -#: ../../common/mount.py:415 -#, python-format +#: common/sshtools.py:489 msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"Tókst ekki að aflæsa ssh-einkalykli. Rangt lykilorð eða að lykilorð er ekki " +"tiltækt fyrir cron." -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "Get ekki aftengt %(proc)s úr %(mountpoint)s" - -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "%(proc)s fannst ekki. Endilega settu upp t.d. %(install_command)s" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Fjartengd slóð er til, en er ekki mappa." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Fjartengd slóð er ekki skrifanleg." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "tengipunkturinn %s ekki laus." +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Fjartengd slóð er ekki keyranleg." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Tókst ekki að útbúa fjartengda slóð." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Fjartengda vélin {host} styður ekki {command}" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "MISTÓKST" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Endurheimta heimildir:" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Fjartengda vélin {host} styður ekki harðtengi (hardlinks)" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Búið" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Afritaðu ssh-dreifilykil \"{pubkey}\" yfir á fjartengdu vélina \"{host}\"." -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "Fresta afritun þegar rafhlöður eru í notkun" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Settu inn lykilorð fyrir \"{user}\"." -#: ../../common/snapshots.py:669 +#: common/tools.py:382 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"Markskráakerfi fyrir {path} er í NTFS-sniði, sem þekkt er fyrir ósamhæfni " +"við Unix-ættuð skráakerfi." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Bið %s sekúndu." -msgstr[1] "Bið %s sekúndur." - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Mistókst að taka skyndiafritið %s !!!" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Geng frá" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} er ekki gild mappa." -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Get ekki búið til möppu: %s" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Gerð eftirfarandi möppu mistókst:" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Vista stillingaskrá ..." +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Skrifaðgangur gæti verið takmarkaður." -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Vista aðgangsheimildir ..." - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"Markskráakerfi fyrir {path} er á FAT-sniði sem virkar ekki með hörðum-" +"tenglum. Notaðu frekar upprunalegt GNU/Linux skráakerfi." -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"Markskráakerfi fyrir {path} er SMB-tengd sameign. Gakktu úr skugga um að " +"fjartengdi SMB-þjónninn styðji tákntengi (symlink) eða virkjaðu {copyLinks} " +"í {expertOptions}." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Get ekki fjarlægt möppu: %s" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Afrita tengla (afbyggja tákntengi - dereference symbolic links)" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Taka skyndiafrit" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Ítarlegri valkostir" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"Markskráakerfi fyrir {path} er í sshfs-tengd sameign. SSHFS styður ekki " +"harða-tengla. Notaðu frekar 'SSH'." -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Gat ekki endurnefnt %(new_path)s sem %(path)s" - -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Snjöll fjarlæging" - -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Fjarlægja gömul skyndiafrit" - -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Reyna að halda í lágmarksmagn af lausu plássi" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Mistókst að útbúa skrá í þessari möppu:" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Reyna að halda a.m.k. %d%% af lausum ihnútum (inodes)" +#: qt/aboutdlg.py:50 +#, fuzzy +msgid "About Back In Time" +msgstr "Back In &Time öryggisafritun" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "MEÐ VILLUM !" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Núna" +#: qt/aboutdlg.py:92 +#, fuzzy +msgid "Authors:" +msgstr "Höfundar" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Get ekki tengt %s" +#: qt/aboutdlg.py:96 +#, fuzzy +msgid "Translators:" +msgstr "Þýðingar" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." msgstr "" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +#: qt/aboutdlg.py:115 +msgid "this link" msgstr "" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s fannst ekki í ssh_known_hosts." - -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" msgstr "" -"Fjartengd slóð er ekki skrifanleg:\n" -" %s" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" msgstr "" -"Fjartengd slóð er ekki keyranleg:\n" -" %s" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -"Tókst ekki að útbúa fjartengda slóð:\n" -" %s" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" msgstr "" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/app.py:332 +#, fuzzy, python-brace-format msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"Það lítur út fyrir að {app_name} sé að keyra í fyrsta skipti þar sem engar " +"grunnstillingar fundust." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/app.py:337 +#, fuzzy msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Flytja inn fyrirliggjandi grunnstillingar (úr markmöppu öryggisafritunar eða" +" annarri tölvu)?" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" +#: qt/app.py:379 +msgid "Then press OK." msgstr "" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/app.py:483 +msgid "Create a backup" msgstr "" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Nota breytingartíma og stærð til að skynja breytingar á skrám." -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Nota gátsummu til að skynja breytingar" +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "Taka skyndiafrit (í gátsummuham)" + +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Nota gátsummu til að skynja breytingar á skrám." -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +#, fuzzy +msgid "Pause backup process" msgstr "Gera hlé á vinnslu skyndiafrits" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +#, fuzzy +msgid "Resume backup process" msgstr "Halda áfram með vinnslu skyndiafrits" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +#, fuzzy +msgid "Stop backup process" msgstr "Stöðva vinnslu skyndiafrits" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Endurnýja lista yfir skyndiafrit" +#: qt/app.py:504 +#, fuzzy +msgid "Refresh backup list" +msgstr "Endurlesa lista yfir skyndiafrit" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Heiti skyndiafrits" +#: qt/app.py:508 +msgid "Name backup" +msgstr "" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" +#: qt/app.py:512 +#, fuzzy +msgid "Remove backup" msgstr "Fjarlægja skyndiafrit" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Skoða skyndiafritaannál" +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "Skoða síðustu atvikaskrá" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Skoða síðasta annál" +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "Að minnsta kosti ein mappa verður að vera undir öryggisafrit." + +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" +msgstr "Skoða síðustu atvikaskrá" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Stillingar" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "" + +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Sýsla með notkunarsnið…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" msgstr "Slökkva" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." msgstr "Slökkva á kerfi þegar skyndiafritun lýkur." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Setja upp tungumál…" + +#: qt/app.py:540 msgid "Exit" msgstr "Hætta" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Hjálp" +#: qt/app.py:550 +#, fuzzy +msgid "man page: Back In Time" +msgstr "Back In &Time öryggisafritun" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Hjálp fyrir stillingaskrá" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Vefsvæði" +#: qt/app.py:555 +#, fuzzy +msgid "man page: Profiles config file" +msgstr "Stillingaskrá notkunarsniða" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Breytingasaga" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" + +#: qt/app.py:573 msgid "FAQ" msgstr "FAQ / Algengar spurningar" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Spyrja spurninga" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Tilkynna um villu" -#: ../../qt/app.py:174 ../../qt/app.py:1439 +#: qt/app.py:584 +msgid "Translation" +msgstr "Þýðingar" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Birtir aftur skilaboðin um þátttöku í þýðingum." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Encryption Transition (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Birtir aftur skilaboðin um fjarlægingu EncFS." + +#: qt/app.py:599 msgid "About" msgstr "Um forritið" -#: ../../qt/app.py:204 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Endurheimta" + +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "Endurheimta valdar skrár eða möppur á upprunalega staðsetningu." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Endurheimta í …" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "Endurheimta valdar skrár eða möppur á nýja staðsetningu." + +#: qt/app.py:615 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "" +"Endurheimta sýnda möppu og allt innihald hennar á upprunalega staðsetningu." + +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "Endurheimta sýnda möppu og allt innihald hennar á nýja staðsetningu." + +#: qt/app.py:624 msgid "Up" msgstr "Upp" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" msgstr "Sýna faldar skrár" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Endurheimta" +#: qt/app.py:630 +#, fuzzy +msgid "Compare backups…" +msgstr "Bera saman skyndiafrit…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Útgáfukandídat" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Endurheimta í ..." +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Birtir aftur skilaboðin um þennan útgáfukandídat." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time öryggisafritun" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Öryggisafrit" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." +#: qt/app.py:717 +msgid "&Restore" +msgstr "Endu&rheimta" + +#: qt/app.py:723 +msgid "&Help" +msgstr "&Hjálp" + +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Skyndiafrit" +#: qt/app.py:784 +msgid "Light icon" +msgstr "" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Skyndiafrit" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Skoða" +#: qt/app.py:808 +msgid "Icons only" +msgstr "" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Flýtilyklar" +#: qt/app.py:811 +msgid "Text only" +msgstr "" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +#: qt/app.py:814 +msgid "Text below icons" msgstr "" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "Bæta við meðfylgjandi" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "Bæta við útilokun" +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." +msgstr "" +"Þessi mappa er ekki til\n" +"í valda skyndiafritinu." -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"Þessi mappa er ekki til\n" +"í valda skyndiafritinu." -#: ../../qt/app.py:492 +#: qt/app.py:995 +#, fuzzy msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Ef þú lokar þessum glugga, mun Back In Time ekki geta slökkt á kerfinu þegar" +" skyndiafritun lýkur." -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +#: qt/app.py:998 +msgid "Close the window anyway?" msgstr "" -#: ../../qt/app.py:667 ../../qt/app.py:719 +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Búið, engin öryggisafritun nauðsynleg" + +#: qt/app.py:1260 msgid "Working:" msgstr "Að vinna:" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Búið, engin öryggisafritun nauðsynleg" +#: qt/app.py:1267 +msgid "Working" +msgstr "Að vinna" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Villa:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Villa" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" msgstr "Sent:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "Hraði:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" msgstr "Áætluð lok:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Víðvært" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Kerfisstjóri (root)" +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "&Öryggisafrit" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Einkamappa (Home)" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Endurheimta {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Öryggisafritunarmöppur" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Endurheimta {path} í …" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Ertu viss um að þú viljir fjarlægja skyndiafritið:\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Hæ\n" +"Þú hefur núna notað Back In Time með {language}-viðmótinu nokkrum sinnum.\n" +"Þýðingarnar á uppsettu útgáfunni á Back In Time fyrir {language} eru {perc} kláraðar. Sama á hvaða stigi þú ert varðandi tækniþekkingu, þá geturðu alveg lagt af mörkum við þýðingarnar og þannig einnig til Back In Time.\n" +"Þú ættir að skoða {translation_platform_url} ef þig langar til að vera með. Til að fá frekari aðstoð og svör við spurningum ættirðu að heimsækja {back_in_time_project_website}.\n" +"Við biðjums velvirðingar á trufluninni, þessi skilaboð munu ekki birtast aftur. Hægt er að skoða þessi skilaboð aftur hvenær sem er úr hjálparvalmyndinni.\n" +"Back In Time teymið" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "þýðingakerfið" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Vefsvæði" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Þýðing þín" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/app.py:1709 +#, fuzzy, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Senda tölvupóst til {link_and_label}." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Póstlisti {link_and_label}" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "Fjarlægja nýjar skrár í upprunalegri möppu" +#: qt/app.py:1718 +#, fuzzy, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Póstlisti {link_and_label}" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} á vefsvæði verkefnisins." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Opna verkbeiðni" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." msgstr "" +"Einnig gætirðu sent inn spurningar á aðra samskiptaleið sem þér líkar." -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" +#: qt/app.py:1841 +msgid "Proceed with the backup?" msgstr "" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -#: ../../qt/app.py:1105 +#: qt/app.py:1875 +#, fuzzy, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Aðvörun{BOLDEND}: Eyðing skráa á rót skráarkerfis gæti skemmt " +"varanlega allt kerfið þitt." -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Skoða innihald á diski" +#: qt/app.py:2107 +#, fuzzy +msgid "Backup name" +msgstr "&Öryggisafrit" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Skyndiafrit: %s" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" +msgstr[1] "" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Skoða skyndiafritið sem búið var til %s" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "" +"Breytingar á tungumálastillingum munu öðlast gildi þegar þú skráir þig næst " +"inn." -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Endurheimta '%s'" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Endurheimta '%s' í ..." +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" +msgstr "" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Höfundar" +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "Geyma nefnd skyndiafrit." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Þýðingar" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" +msgstr "" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Notkunarleyfi" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Spurning" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." +msgstr "" + +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" +msgstr "" + +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" + +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Fjarlægja nýjar skrár í upprunalegri möppu." + +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" + +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Ertu viss um að þú viljir endurheimta þetta atriði í nýju möppuna?" +msgstr[1] "Ertu viss um að þú viljir endurheimta þessi atriði í nýju möppuna?" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Ertu viss um að þú viljir endurheimta þetta atriði ?" +msgstr[1] "Ertu viss um að þú viljir endurheimta þessi atriði?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" msgstr "" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Hafa með skrár og möppur" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Bæta við meðfylgjandi" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Bæta við útilokun" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Setja upp tungumál" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Þýtt: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Sjálfgefið í kerfinu" + +#: qt/languagedialog.py:142 +#, fuzzy +msgid "Use operating system's language." +msgstr "Nota tungumál stýrikerfis." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "Síðasta virka sýn atvikaskráningar" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Síðasta virka sýn atvikaskráningar" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" -msgstr "Snið:" +msgstr "Notkunarsnið:" + +#: qt/logviewdialog.py:86 +#, fuzzy +msgid "Backups:" +msgstr "&Öryggisafrit" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Sía:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Villa, [I] Upplýsingar, [C] Breyta" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "afkóða slóðir" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Allt" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Breytingar" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Villur" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Breytingar" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Upplýsingar" +msgstr[1] "Upplýsingar" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Upplýsingar" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Villa, [I] Upplýsingar, [C] Breyta" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Sýsla með notkunarsnið" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "afkóða slóðir" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Breyta" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Bæta við" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Fjarlægja" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Almennt" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "Ta&ka með" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Undanskilja" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "Valk&ostir" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "Ítarlegri &valkostir" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Endurheimta stillingaskrá" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Nýtt snið" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Endurnefna snið" + +#: qt/manageprofiles/__init__.py:215 +#, fuzzy, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Ertu viss um að þú viljir eyða sniðinu \"{name}\" ?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Villa" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "Gera hlé á vinnslu skyndiafrits" + +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "Undanskilja skrá" + +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Öryggisafritunarmöppur" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "Get ekki fjarlægt möppu" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Sýna faldar skrár" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Stöðva vinnslu skyndiafrits" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Undanskilja möppu" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "sjálfgefið" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" +msgstr "Vinnuáætlun" + +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" +msgstr "Dagur:" + +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" +msgstr "Vikudagur:" + +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Tími:" + +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" +msgstr "Klukkustundir:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:98 +msgid "" +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" +msgstr "Hverjar:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Virkja atvikaskráningu villuleitarskilaboða" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:120 +msgid "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Óvirkt" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Við hverja ræsingu/endurræsingu" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Á {n} mínútu fresti" +msgstr[1] "Á {n} mínútna fresti" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Á klukkustundar fresti" +msgstr[1] "Á {n} klukkustunda fresti" + +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Sérsniðnar klukkustundir" + +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Hvern dag" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Endurtekið (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Þegar drif er tengt (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Vikulega" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Mánaðarlega" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Árlega" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "klukkustund(ir)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "dag(a)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "viku(r)" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "mánuð(ir)" + +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Più vecchi di:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Seleziona un file di chiave privata esistente da qualche altra parte." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Se lo spazio libero è minore di:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "Se gli inode liberi sono meno del:" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Crea una nuova chiave SSH senza frase segreta." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "Esegui in modo asincrono sull'host remoto" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Percorso completo: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "Mantieni tutte le istantanee degli ultimi" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Chiave privata:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "giorno/i" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Usa la configurazione SSH di sistema" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "Mantieni un'istantanea al giorno per gli ultimi" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" +"Lascia il file chiave non selezionato. Le connessioni SSH si baseranno sulla" +" configurazione client di sistema esistente (ad es., ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "Mantieni un'istantanea alla settimana per le ultime" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Proxy SSH" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "Settimana/e" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Host:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "Mantieni un'istantanea al mese per gli ultimi" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Porta:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "mese/i" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Utente:" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "Mantieni un'istantanea all'anno per tutti gli anni" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." +msgstr "" +"Connetti all'host destinazione attraverso questo proxy (conosciuto anche " +"come jump host). Per i dettagli, consulta \"-J\" nella documentazione del " +"comando \"ssh\" o \"ProxyJump\" nella pagina del manuale." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Non rimuovere le istantanee a cui è stato assegnato un nome" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Info{ENDBOLD}: nella modalità 'SSH cifrato' funzionano solo gli " +"asterischi singoli e doppi (es. {example2}). Gli altri tipi di caratteri " +"jolly e pattern saranno ignorati (es. {example1}). In questa modalità i nomi" +" di file non sono prevedibili a causa della cifratura di EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Escludi pattern, file o cartelle" + +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Aggiungi pattern" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Aggiungi file" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opzioni" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Aggiungi cartelle" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Attivare le notifiche" +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Suggerimenti" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Disabilita le istantanee quando si usa la batteria" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "" +"Seleziona tra gli elementi comunemente utilizzati da aggiungere all'elenco " +"delle esclusioni." -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Impossibile stabilire la modalità di alimentazione" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Escludi file più grandi di:" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "Esegui solo un'istantanea alla volta" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Escludi file più grandi del valore in {size_unit}." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" -"Le altre istantanee verranno bloccate finché l'istantanea corrente non sarà " -"terminata.\n" -"Questa è un'opzione globale, pertanto avrà effetto su tutti i profili di " -"questo utente.\n" -"Tuttavia devi attivare quest'opzione anche per gli altri utenti." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Con la 'Modalità rsync completo' è disabilitata, questa impostazione avrà " +"effetto solo sui nuovi file, perché rsync la tratta come un'opzione di " +"trasferimento, non come una regola di esclusione. Per questo motivo i file " +"già sottoposti a backup in precedenza rimarranno nei backup anche se sono " +"stati modificati." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Pattern di esclusione" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "Esegui il backup dei file sostituiti durante il ripristino" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Digita un pattern di esclusione:" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Continua in caso di errore (mantiene un'istantanea incompleta)" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Per aiuto, vedi la sezione {link} della pagina di manuale di rsync." -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" -"Prendi un'istantanea anche se non ci sono modifiche rispetto alla precedente." +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Aprire la pagina di manuale per rsync" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Livello dei log" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Escludi file" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Nulla" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Escludi cartelle" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Modifica & Errori" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" +"Disabilitato perché questo pattern non è funzionante in modalità 'SSH " +"cifrato'." -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Cambiare questo opzioni solo se si sa cosa si sta facendo !" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"Queste opzioni sono pensate per le configurazioni avanzate. Modificale solo " +"sei pienamente consapevole delle loro implicazioni." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "Esegui 'nice':" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Esegui 'rsync' con '{cmd}':" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" msgstr "come cron job" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" msgstr "sull'host remoto" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "Esegui 'ionice':" - -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "quando si prende un'istantanea manuale" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "quando si crea un backup manuale" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "Esegui 'rsync' con 'nocache':" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Installa 'nocache' per abilitare questa opzione." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" msgstr "sulla macchina locale" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "Redirigi stdout verso /dev/null nei cronjob" +msgstr "Redirigi stdout verso /dev/null nei cronjob." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" +"Cron invierà automaticamente una email con allegato il registro dei cronjob " +"se un MTA è installato." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "Redirigi stderr verso /dev/null nei cronjob" +msgstr "Redirigi stderr verso /dev/null nei cronjob." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "Limita la banda utilizzata da rsync: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" +"Cron invierà automaticamente una email con allegato il registro errori dei " +"cronjob se un MTA è installato." + +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/s" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr " KB/s" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Limita la banda utilizzata da rsync:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "Preserva ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Preserva attributi estesi (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" -"Copia collegamenti non verificati (funziona sono con collegamenti assoluti)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Limita a un file system" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Le opzioni devono essere racchiuse tra virgolette, es. {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "Passa opzioni addizionali ad rsync" +msgstr "Passa opzioni aggiuntive a rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefisso da eseguire prima di ciascun comando sull'host remoto." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -"Le ozioni devono essere racchiuse tra virgolette, ad esempio --exclude-" -"from=\"/percorso/verso/il/mio file escluso\"." +"Le variabili necessitano di escaping con \\$FOO. Questo non riguarda rsync. " +"Quindi per aggiungere un prefisso per rsync usa \"{example_value}\" con " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "default" + +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" -msgstr "Aggiungi il prefisso ai comandi SSH" +msgstr "Aggiungi prefisso ai comandi SSH" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Controlla se l'host remoto è online" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Attenzione: se disabilitato e l'host remoto non è disponibile, questo " +"potrebbe portare ad alcuni errori strani." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "default" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Controlla se l'host remoto supporta tutti i comandi necessari." -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Attenzione: se disabilitato e l'host remoto non supporta tutti i comandi " +"necessari, questo potrebbe portare ad alcuni strani errori." + +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(predefinito: {})" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "disabilitato" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "abilitato" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Modalità:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Dove salvare i backup" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Impostazioni di SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Percorso:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "File della chiave:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Password" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Salva la password nel gestore delle password" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" +"Salva la password per Cron (problema di sicurezza: l'utente root può leggere" +" la password)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avanzate" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Percorso completo per i backup:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" +"La pianificazione è stata disabilitata perchè cron non risulta installato. " +"Installare cron per abilitare i backup pianificati." + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Il percorso di destinazione del backup non può essere vuoto." + +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "La password di cifratura non può essere vuota." + +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"Si è verificato un errore durante il tentativo di accesso all'host remoto. È" +" stato restituito il seguente messaggio di errore:" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Per abilitare il login senza password, la chiave pubblica SSH può essere " +"copiata nell'host remoto." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Vuoi procedere con la copia della chiave SSH?" + +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Impossibile copiare la chiave SSH pubblica. Potrebbe essere dovuto a un " +"problema di connessione o di permessi." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "Ripristina la configurazione" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "L'autenticità dell'host \"{host}\" non può essere stabilita." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "L'impronta digitale della chiave {keytype} è:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Verifica questa impronta digitale. Vuoi aggiungerla al file 'known_hosts'?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Vuoi cambiare la cartella del backup?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "La destinazione selezionata per il backup non è vuota." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Per utilizzare la cifratura deve essere vuoto." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "File non valido: non è una chiave privata SSH" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"Il file selezionato ({path}) è una chiave pubblica SSH. Scegli il file della" +" chiave privata corrispondente (senza estensione \".pub\")." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Nuovo profilo" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" +"Il file {path} esiste già. Impossibile creare una nuova chiave SSH con tale " +"nome." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Rinomina profilo" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Impossibile creare una nuova chiave SSH in {path}." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Sei sicuro di voler eliminare il profilo \"%s\" ?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Includi file e cartelle" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" -"Le ore personalizzate possono essere solo un elenco di ore separate da " -"virgola (es. 8,12,18,23) oppure */3 per backup periodici ogni 3 ore" +"\"{path}\" è un collegamento simbolico. La destinazione del collegamento non" +" sarà copiata nel backup se non inclusa esplicitamente." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Vuoi includere invece la destinazione del collegamento simbolico?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Includi file" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Includi cartelle" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Abilita le notifiche" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Disabilita i backup quando usi la batteria" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Impossibile stabilire la modalità di alimentazione" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Esegui solo un backup alla volta" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:56 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Gli altri backup saranno bloccati finché il backup attuale non sarà " +"completato. Questa è un'opzione globale, pertanto avrà effetto su tutti i " +"profili di questo utente. Tuttavia, devi attivare quest'opzione anche per " +"gli altri utenti." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Backup dei file sostituiti durante il ripristino" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Continua in caso di errore (mantieni backup incompleti)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Usa il checksum per rilevare i cambiamenti" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Crea un nuovo backup in presenza di modifiche o meno." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Avvisa se lo spazio libero sul disco scende sotto" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Mostra un avviso quando lo spazio libero sul disco di destinazione del " +"backup è inferiore al valore specificato." -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:103 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Se la politica di rimozione e conservazione è abilitata e i vecchi backup " +"vengono rimossi in base allo spazio libero disponibile, questo valore non " +"può essere inferiore al valore impostato nella politica." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Livello di log:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nessuno" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "manuale utente" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Le regole seguenti sono elaborate dall'alto in basso. Le regole successive " +"scavalcano le regole precedenti. Vedi il {manual_link} per dettagli ed " +"esempi." + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Apri il manuale utente nel browser." + +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Mantieni il backup più recente." + +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Il backup più recente è mantenuto in ogni caso." -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Il comportamento non può essere modificato." + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Mantieni i backup con un nome." + +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"I backup che, in aggiunta alla consueta marca temporale, hanno ricevuto un " +"nome saranno mantenuti in ogni caso e non saranno rimossi." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Pattern di esclusione" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Anno(i)" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Rimuovi i backup più vecchi di" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Giorni interi. Il giorno attuale è ignorato." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" +"Settimane di calendario con lunedì come primo giorno. La settimana attuale è" +" ignorata." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "File esclusi" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Periodi di 12 mesi. Il mese attuale è ignorato." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Cartelle Escluse" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Politica di conservazione" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Includi il file" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Esegui in sottofondo sull'host remoto." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:313 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" -"\"%s\" è un link simbolico. La destinazione del link non sarà copiata nel " -"backup fintantoché non la includerai esplicitamente.\n" -"Vuoi piuttosto includere la destinazione del link simbolico?" +"La procedura di rimozione sarà eseguita direttamente nella macchina remota, " +"non localmente. I comandi \"bash\", \"screen\" e \"flock\" devono essere " +"installati e disponibili sulla macchina remota." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Cartella inclusa" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "" +"Se selezionato, Back In Time effettuerà innanzi tutto un test della macchina" +" remota." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "I giorni sono contati a partire da oggi." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Mantieni tutti i backup degli ultimi" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Cambiare veramente la cartella delle istantanee?" +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "giorno/i." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Mantieni l'ultimo backup per ogni giorno per gli ultimi" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Le settimane sono contate a partire dalla settimana attuale in corso. Una " +"settimana inizia il lunedì." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "abilitato" +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Mantieni l'ultimo backup per ogni settimana per le ultime" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "disabilitato" +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "settimana/e." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "" +"I mesi sono contati come mesi di calendario a partire dal mese attuale." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "Ripristina impostazioni" +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Mantieni l'ultimo backup per ogni mese per gli ultimi" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr " ed aggiungi il tuo utente al gruppo 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "mese/i." -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:370 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The years are counted as calendar years starting with the current year." msgstr "" -"Per favore localizza l'istantanea dalla quale vuoi ripristinare la " -"configurazione di %(appName)s. Il percorso potrebbe assomigliare a:\n" -"%(samplePath)s\n" -"\n" -"Se le tue istantanee sono su un'unità remota o se sono cifrate, devi prima " -"montarle manualmente. Se usi la modalità SSH, potresti anche aver bisogno di " -"configurare l'accesso all'host remoto %(addFuse)s mediante chiave pubblica.\n" -"Dai un'occhiata a: 'man backintime'." +"Gli anni sono contati come anni di calendario a partire dall'anno attuale." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Mantieni l'ultimo backup per ogni anno per" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "tutti gli anni." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… lo spazio libero è inferiore a" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… gli inode liberi sono inferiori a" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Rimuovi i vecchi backup se…" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "Configurazione non trovata" +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "Per eseguire Back In Time come root è richiesta l'autenticazione." -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" +"Per modificare le pianificazioni di backup attivate dalle connessioni a " +"dispositivi di archiviazione esterni è richiesta l'autenticazione." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Collegamenti" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Risorse" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "File System" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Cartelle di backup" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profilo: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profilo: {profile_name} (dall'utente \"{desktop_user}\")" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Visualizza ultimo log" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Avvia {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Lavoro in corso…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "pagina del manuale: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importa configurazione" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Nessuna cartella selezionata" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importa" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Ricerca…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Seleziona la cartella dei backup da cui importare la configurazione. Il " +"percorso potrebbe somigliare a: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" +"Se la cartella si trova su un disco esterno o remoto, deve essere prima " +"montato manualmente." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Analizza nuovamente" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Mostra le cartelle nascoste" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Opzioni Diff" +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Mostra/nascondi le cartelle nascoste (Ctrl+H)" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Nessun configurazione trovata in questa cartella" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Ricerca completata." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Mostra il registro completo" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Conto alla rovescia per lo spegnimento" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Il backup è terminato." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Annulla lo spegnimento" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Spegni subito" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Il sistema si spegnerà tra {n} secondo." +msgstr[1] "Il sistema si spegnerà tra {n} secondi." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Opzioni relative al confronto tra backup" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Comando:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametri:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "Usare %1 e %2 per i parametri del percorso" +msgstr "Usa %1 e %2 come parametri del percorso" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Si prega di impostare un comando diff o premere Annulla." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Elenca solo istantanee differenti" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Il comando \"{cmd}\" non è stato trovato nel sistema. Si prega di provarne " +"un altro o premere Annulla." + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" +"Nessun parametro impostato per il comando diff. Uso il valore predefinito " +"\"{params}\"." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Backup" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Solo backup diversi" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "Mostra solo istantanee uguali a: " +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Mostra solo i backup uguali a:" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Controllo approfondito (più accurato ma più lento)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "Elimina" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "Seleziona tutti" +msgstr "Seleziona tutte" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Confronta" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Vai a" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Non è possibile confrontare un'istantanea con se stessa" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opzioni" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Impossibile confrontare un backup con se stesso, poiché il confronto sarebbe" +" ridondante." -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Comando non trovato: %s" +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Vuoi davvero eliminare {file_or_dir} nel backup {backup_id}?" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Vuoi davvero eliminare {file_or_dir} in {count} backup?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "ATTENZIONE: Questa operazione è irreversibile." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Vuoi escludere {path} dai backup futuri?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Modalità Root" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -"Vuoi veramente cancellare \"%(file)s\" nell'istantanea \"%(snapshot_id)s?\n" +"Back In Time è attualmente in esecuzione con i privilegi di root (accesso " +"completo al sistema)" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" -msgstr "Vuoi veramene cancellare \"%(file)s\" in %(count)d istantanee?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Oggi" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "ATTENZIONE: l'operazione è irreversibile" +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Ieri" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Questa settimana" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Scorsa settimana" + +#: qt/timeline.py:88 +msgid "This month" +msgstr "" + +#: qt/timeline.py:95 +msgid "Last month" +msgstr "" + +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Ultimo controllo {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "Escludi \"%s\" dalle istantanee future?" +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Questo NON è un backup, bensì una vista attuale dei tuoi file locali." diff --git a/common/po/ja.po b/common/po/ja.po index 26037abdf..dee2bae90 100644 --- a/common/po/ja.po +++ b/common/po/ja.po @@ -1,1656 +1,2458 @@ -# Japanese translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © AmaseCocoa (甘瀬ここあ) +# SPDX-FileCopyrightText: © Ayako +# SPDX-FileCopyrightText: © Sadaharu Wakisaka # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:24+0000\n" -"Last-Translator: Toshiharu Kudoh \n" -"Language-Team: Japanese \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-05 17:33+0000\n" +"Last-Translator: buhtz \n" +"Language-Team: Japanese \n" +"Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"このプロジェクトで使われる全てのライセンスは {dir_link} " +"ディレクトリにあります。ファイル単位のライセンス及び著作権をSPDXメタデータから取得するには、{readme_link} を参照してください。" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "警告" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "プロファイル \"%s\" はすでに存在しています!" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "メインプロファイル" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "最後のプロファイルを削除することが出来ません!" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "ローカル (EncFS暗号化)" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "無効" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS暗号化)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "起動/再起動毎に" +#: common/config.py:237 +msgid "Local" +msgstr "ローカル" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "5分毎" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "ローカル暗号化" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "10分毎" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "暗号" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH 秘密鍵" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "プロファイル: 「{name}」" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "バックアップフォルダが無効です。" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "最低でも一個のディレクトリをバックアップのために指定してください。" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "ディレクトリ: {path}" + +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "このディレクトリはバックアップ先の一部であるため、バックアップに含めることはできません。" -#: ../../common/config.py:87 -msgid "Custom Hours" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"「空き容量が以下になった場合に最古のバックアップを削除する」の値 ({val_one}) は、「空きディスク容量が以下になった場合に警告する」のしきい値" +" ({val_two}) 以下でなければなりません。" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "毎日" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "バックアップ削除制限が警告制限を超えないよう設定を調整してください。" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "{mode} モード では、udev スケジュールは使用できません" + +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "新しい crontab の書き込みに失敗しました。" + +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"crontab コマンドは利用可能ですが Cron は稼働していません。予定されたバックアップ job は実行されないでしょう。 Cron " +"はインストールされているようですが、サービスとして起動されてない可能性があります。 2つのコマンド 'systemctl enable cron' と " +"'systemctl start cron' を実行してみてください、もしくは使用している GNU/Linux " +"ディストリビューションのサポートに相談するのもよいでしょう。" + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "設定を保存できませんでした" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "設定を読み込めませんでした" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "プロファイル 「{name}」 はすでに存在しています。" + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "最後に残ったプロファイルを削除することはできません。" + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "「{command}」 をマウントできません" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "フォルダの暗号化に関する設定がみつかりません。" + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "新しい暗号化されたフォルダを作成しますか?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "取消" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "EncFSのパスワードを再入力してください。" + +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFSのパスワードが一致しません。" + +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "スナップショット取得" + +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "暗号化されたパス「{command}」の初期化に失敗しました" + +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "{mountprocess} を {mountpoint} からアンマウントできません。" + +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "{command} は見つかりません。例を参考にインストールしてください。例 「{installcommand}」" + +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "マウントポイント {mntpoint} が空ではありません。" + +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "{mode}プロファイル 「{profile}」 のパスワードを入力してください:" + +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" +"プロファイル {profile_id} のUdevルールをインストールできませんでした。 DBusサービス '{dbus_interface}' " +"が有効ではありません。" -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "毎週" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "{path} には該当の UUID がありませんでした" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "毎月" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "失敗" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "日" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "パーミッションを復元" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "週" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "完了" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "年" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "以下のインクルードリストのエントリには、バックアップソースに対応するファイルまたはディレクトリが存在しません:" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "バッテリー動作のときは延期する" + +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "バックアップフォルダが見つかりません。" + +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "もしリムーバブルドライブ上にあるのなら、それを接続してください。" + +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "{n} 秒待機中。" + +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "バックアップ {snapshot_id} の作成に失敗しました。" + +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "最終処理を行っています、少々お持ちください…" + +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "フォルダを作成できません。" + +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "設定ファイルの保存…" + +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "パーミッションを保存…" + +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "以前に作成した継続使用できるバックアップ {snapshot_id} がありました。" + +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "前回の作業で作成した不完全な {snapshot_id} フォルダを削除しています" + +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "フォルダを削除できません" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "バックアップを作成中" + +#: common/snapshots.py:1517 +msgid "Success" +msgstr "成功" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "エラーによる部分的な転送" + +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "ソースファイルの消失による部分的な転送 (man rsync を参照)" + +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' は終了コード {exit_code} で終了しました" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "詳細は'man rsync'を参照" + +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "ネガティブの rsync 終了コードはシグナル番号です、'kill -l' および 'man kill' を参照してください" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "変更箇所がないので新しいバックアップは必要ありません" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "{path} を {new_path} に変更できません。" -#: ../../common/config.py:129 -msgid "Local" -msgstr "" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "古いバックアップを削除するためのルールの適用" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "保持ポリシーの適用" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "最小限のフリースペースの確保を試みる" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "最低限でも{perc}分のフリーなinodeを確保中です" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "現在" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "{sshfs} をマウントできません" -#: ../../common/config.py:135 -msgid "Default" -msgstr "" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agentが見つかりません。インストールされているか確認してください。" -#: ../../common/config.py:136 -msgid "AES128-CTR" +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "ssh の秘密鍵を解除できませんでした。パスワードが間違っているか、cron で使用できないパスワードです。" + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "リモートパスは存在するが、ディレクトリではない。" + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "リモートパスは書き込みできません。" + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "リモートパスは実行可能ではありません。" + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "フォルダを作成できません。" + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "リモートホスト {host} は {command} をサポートしていない" + +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "ホスト {host} でのコマンド確認中に不明なエラーが発生しました" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "リモートホスト {host} がハードリンクをサポートしていません" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "公開鍵 「{pubkey}」 をリモートホスト 「{host}」 にコピーしてください。" + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "ユーザ 「{user}」のパスワードを入力してください。" + +#: common/tools.py:382 +#, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"{path} の保存先ファイルシステムは NTFS でフォーマットされていて、これはUnix-" +"styleのファイルシステムと非互換であることが知られています。" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} は有効なディレクトリではありません。" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "以下のディレクトリの作成に失敗しました:" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "書き込み権限が限定されているようです。" -#: ../../common/config.py:140 -msgid "ARCFOUR128" +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"{path} の保存先ファイルシステムは、ハードリンクをサポートしない FAT でフォーマットされています。ネイティブの GNU/Linux " +"ファイルシステムを使用してください。" -#: ../../common/config.py:141 -msgid "AES128-CBC" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"{path} の保存先ファイルシステム は SMB マウントされた共有です。 " +"リモートSMBサーバーがシンボリックリンクをサポートしているか確認してください、または {expertOptions} で {copyLinks} " +"を有効化してください。" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "リンクをコピーする(シンボリックリンクの参照解除)" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "上級者向けオプション" -#: ../../common/config.py:144 -msgid "Cast128-CBC" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"{path} の保存先ファイルシステムは sshfs の共有マウントです。 sshfs はハードリンクをサポートしていません 。代わりにモード " +"「SSH」 を使用してください。" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "以下のディレクトリへのファイル作成が失敗しました:" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Back In Time について" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "著作権:" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "メインプロファイル" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "開発者:" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "プロファイル: \"%s\"" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "翻訳:" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "スナップショットのフォルダは有効ではありません!" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "" +"AmaseCocoa (甘瀬ここあ) \n" +"Ayako\n" +"Sadaharu Wakisaka " -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "バックアップするためには最低1つのフォルダを選択しなくてはなりません!" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "この言語の翻訳者一覧はありません。" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "バックアップフォルダを含むことはできません!" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "このリンク" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "バックアップサブフォルダを含むことはできません!" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "全言語の翻訳者一覧は {thislink} にあります。" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s はフォルダではありません!" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "開発元サイト" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "ユーザーマニュアル" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"%s に書き込めません。\\n\n" -"書き込み権限を持っていますか?" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "ユーザーマニュアルをブラウザで開く(ローカルファイルがある場合はローカルファイル、ない場合はオンライン)" + +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}バージョン{BOLDEND}: {version}" -#: ../../common/config.py:402 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +"{app_name} appears to be running for the first time because no configuration" +" is found." +msgstr "{app_name} は設定が見つからないため、初めて実行されているようです。" -#: ../../common/config.py:407 -#, python-format +#: qt/app.py:337 msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" +"Import an existing configuration from a backup location or another computer?" +msgstr "既存の設定をバックアップ場所または別のコンピューターからインポートしますか?" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "リンクをコピー(シンボリックリンクを参照)" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "次に「OK」を押してください。" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "上級者向けオプション" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "バックアップを作成" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "ファイルの変更検出には、変更時間とサイズを使用します。" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"crontab がみつかりません。\\n\n" -"cron をインストールしてもよろしいですか?\\n\n" -"インストールしないのであれば、全ての自動バックアップは無効になります。" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "バックアップを作成(チェックサムモード)" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "変更の検出にチェックサムを使用する。" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "バックアップの作成を一時停止する" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "バックアップの作成を再開する" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "バックアップの作成を停止する" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "バックアップのリストを更新" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "バックアップに名前をつける" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "バックアップの削除" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "バックアップログを開く" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "選択したバックアップのログを表示します。" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "最後のバックアップログを開く" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "最新のバックアップのログを表示する。" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "スナップショット取得" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "プロファイルの管理…" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "ユーザーコールバックの編集" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: qt/app.py:532 +msgid "Shutdown" +msgstr "電源を切る" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "バックアップの作成終了後に電源を切る。" -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" +#: qt/app.py:536 +msgid "Setup language…" +msgstr "言語設定…" -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: qt/app.py:540 +msgid "Exit" +msgstr "終了" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man ページ: Back In Time" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Back In Time (backintime)の man ページを表示する" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man ページ: 設定ファイルのプロファイル" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "設定ファイル (backintime-config) のプロファイルに関する man ページを表示する" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Back In Time の開発サイトをブラウザで開く" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "完了" +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "変更履歴" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "変更履歴を開く(ローカルで利用可能な場合はローカルから、そうでない場合はウェブから)" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" -"スナップショットフォルダがみつかりません。\\n\n" -"もしリムーバブルドライブ上にあるのなら、接続して下さい。" - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "%s 秒待っています。" - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "スナップショット %s の取得に失敗しました!!!" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "ファイナライズ中" - -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "フォルダ %s を作成できません" - -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "設定ファイルを保存..." - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "パーミッションを保存..." - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "…" - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/app.py:573 +msgid "FAQ" +msgstr "よくある質問" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "よくある質問 (FAQ) をブラウザで開く" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "フォルダ %s を削除できません" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "質問する" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "スナップショット取得" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "バグを報告する" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: qt/app.py:584 +msgid "Translation" +msgstr "翻訳" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "翻訳への参加に関するメッセージを再度表示。" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "賢く削除" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "暗号化の移行 (EncFS)" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "古いスナップショットを削除" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "EncFS 削除に関するメッセージを再度表示。" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "最小限のフリースペースの確保を試みる" +#: qt/app.py:599 +msgid "About" +msgstr "バージョン情報" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "復元" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "" +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "選択したファイルまたはディレクトリを元の場所に復元します。" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "現在" +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "復元…" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "選択したファイルまたはディレクトリを新しい場所に復元します。" -#: ../../common/sshtools.py:315 +#: qt/app.py:615 msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "現在表示されているディレクトリとそのすべての内容を元の場所に復元します。" -#: ../../common/sshtools.py:345 -#, python-format +#: qt/app.py:621 msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "現在表示されているディレクトリとその全内容を新しい場所に復元します。" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "上のフォルダへ" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "不可視ファイルを表示" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "バックアップを比較…" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "リリース候補版" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "リリース候補版についてのメッセージを再度表示。" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "バックアップ(&B)" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "復元(&R)" -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "ヘルプ(&H)" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "システムトレイアイコン" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:780 +msgid "Automatic" +msgstr "自動" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:784 +msgid "Light icon" +msgstr "ライトアイコン" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "変更の検出にチェックサムを使用" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "ダークアイコン" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:808 +msgid "Icons only" +msgstr "アイコンのみ" + +#: qt/app.py:811 +msgid "Text only" +msgstr "テキストのみ" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "テキストをアイコンの下に" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "テキストをアイコンの横に" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"現在選択されているバックアップには\n" +"このフォルダはありません。" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"現在選択されているバックアップには\n" +"このフォルダはありません。" + +#: qt/app.py:995 +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." +msgstr "このウインドウを閉じると Back In Time はバックアップの作成終了時に電源を切ることができなくなります。" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "スナップショットリストを更新" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "本当に閉じますか?" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "スナップショット名" +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "完了、バックアップの必要はありませんでした" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "スナップショット削除" +#: qt/app.py:1260 +msgid "Working:" +msgstr "作業中:" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "スナップショットのログを閲覧" +#: qt/app.py:1267 +msgid "Working" +msgstr "作業中" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "最近のログを閲覧" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "エラー" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "設定" +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "送信:" -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "速度:" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "完了予想時間:" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "終了" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "バックアップ:" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "ヘルプ" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "{path} を復元" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "{path} を復元…" -#: ../../qt/app.py:163 +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"こんにちは\n" +"これまでに Back In Time {language} 版をご利用いただいた皆様へ。\n" +"インストールいただいた Back In Time {language} 版のバージョンでは、{perc} まで翻訳が完了しています。技術的な知見の有無に関わらず、翻訳にご協力いただくことで Back In Time に貢献いただくことが可能です。\n" +"ご協力いただける場合は {translation_platform_url} をご覧ください。その他のヘルプやご質問は {back_in_time_project_website} をご覧ください。\n" +"突然のメッセージを失礼致しました。このメッセージは今後表示されませんが、ヘルプメニューからいつでもご覧いただけます。\n" +"Back In Time チーム一同" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "翻訳プラットフォーム" + +#: qt/app.py:1658 msgid "Website" msgstr "ウェブサイト" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "翻訳へのご協力のお願い" -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Mastodon にある Fediverse で: {link_and_label}。" -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "{link_and_label}宛のメール。" -#: ../../qt/app.py:171 -msgid "Report a bug" -msgstr "" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "メーリングリスト {link_and_label}。" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "情報" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "開発サイトに {link_and_label}。" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "上層へ" +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "議題を伝える" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "不可視ファイルを表示" +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "その他に、好みのチャンネルを選択することもできます。" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "復元" +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"このバーションの Back In Time は公開予定版で次の公式リリースに向けて安定性のテストに使用されます。\n" +"個人情報等のユーザーデータは送信されません、しかしながら、 Back In Time チームは公開予定版が使用されているかどうかや、このようなプレリリース版を提供することに意味があるのかどうか知りたいと思っています。\n" +"それ故に、もし何も問題なくてもフィードバックをくださることをお願いしたいのです。 たった数分間のテストランでも十分に有益な情報となり得ます。\n" +"以下は送信要領です:\n" +"{contact_list}\n" +"このバージョンではメッセージは再度表示されません、が、ヘルプメニューからいつでもアクセスできます。\n" +"Back In Time をご愛用いただき、多大なる支援を頂いて誠に感謝申し上げます。\n" +"Back In Time チーム一同" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." +msgstr "宛先には {free} の空き領域しかなく、これは設定されたしきい値 {threshold} を下回っています。" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "バックアップを続行しますか?" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "{path} にあるより新しいファイルは削除されます。続行しますか?" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "元のディレクトリにあるより新しいファイルは削除されます。続行しますか?" -#: ../../qt/app.py:234 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." +msgstr "{BOLD}警告{BOLDEND}: ファイルシステムルートのファイルを削除すると、システム全体が壊れる可能性があります。" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "バックアップ名" + +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "このバックアップを削除しますか?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "言語設定は、Back In Time を再起動した後に有効になります。" -#: ../../qt/app.py:249 -#, python-format +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "バージョン管理されたバックアップ (ルート)" + +#: qt/backintime-qt-root.desktop:23 msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" +msgstr "バージョン管理されたバックアップのためのユーザーフレンドリーなGUI(ディスク使用量を削減)(rootモード)" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "スナップショット" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "バージョン管理されたバックアップ" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" +msgstr "バージョン管理されたバックアップのための使いやすいGUIで、ディスク使用量を削減します" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "質問" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "ショートカット" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." +msgstr "ローカル要素を上書きまたは削除する前に、末尾に {suffix} を付加したバックアップコピーを作成します。" -#: ../../qt/app.py:337 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"復元をする際に、より新しいファイルがある場合は拡張子 {suffix} がファイル名に付与されます。もし不要の場合は次のコマンドで削除してください :" + +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "存在しない要素、または宛先にある要素よりも新しい要素のみを復元します。「{rsync_example}」オプションを使用します。" -#: ../../qt/app.py:398 +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "元のフォルダ内の新しいファイルを削除する。" + +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"選択したファイルやフォルダを元の保存先に復元して、バックアップに含まれないファイルやフォルダを削除します。これによってバックアップ作成時に除外されたファイルやフォルダが" +" 削除されるので、十分注意してください。" + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "本当にこれらを新しいディレクトリに復元しますか?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "本当にファイルを復元しますか?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "ユーザーコールバック: 「{filename}」" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "ユーザーコールバックスクリプトは、最初の行にシェーバンを含める必要があります(例:{example})。" + +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "隠しファイルと隠しディレクトリを表示/非表示にする (Ctrl+H)" + +#: qt/fileview.py:204 msgid "Add to Include" -msgstr "" +msgstr "含むフォルダを指定" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 +#: qt/fileview.py:205 msgid "Add to Exclude" -msgstr "" +msgstr "除外するフォルダを指定" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "よく使うアイテムから選択して除外リストに追加してください。" + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "よく使うアイテムから選択して除外リストに追加してください。" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "言語設定" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "翻訳済み: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "標準設定" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "オペレーティングシステムの言語を使用する。" + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "バックアップログを表示" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "最終ログを表示" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" -msgstr "" +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "プロファイル:" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" -msgstr "" -"スナップショットフォルダがみつかりません。\\n\n" -"もしリムーバブルドライブ上にあるのなら、接続後に OK を押して下さい。" +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "バックアップ:" -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" -msgstr "" +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "フィルタ:" -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "作業中:" +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] エラー、 [I] インフォメーション、 [C] 変更点" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "完了、バックアップの必要はありませんでした" +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "復号先" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "エラー:" +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "全て" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "変更点" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "エラー" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" -msgstr "" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "情報" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "全体" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync 転送の失敗 (実験的)" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "ルート" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "プロファイルの管理" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "ホーム" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "編集" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "バックアップフォルダ" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "追加" -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"スナップショット %s を\\n\n" -"削除してもよろしいですか" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "削除" -#: ../../qt/app.py:1025 -#, python-format +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "一般(&G)" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "追加リスト(&I)" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "除外リスト(&E)" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "削除と保持の条件(&R)" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "オプション(&O)" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "上級者向けオプション(&X)" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "設定の復元" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "新規プロファイル" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "プロファイルをリネーム" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "プロファイル \"{name}\" を削除しますか?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "シンボリックリンクをファイルとしてコピーする" + +#: qt/manageprofiles/copylinkswidget.py:43 msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"シンボリックリンクを実際のファイルまたはディレクトリとしてバックアップにコピーします。すべてのリンクをコピーするか、ソース外を指すリンクのみをコピーするかを選択します。" + +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "このオプションはバックアップサイズを増加させる可能性があります。" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "デフォルトで無効化されています。" + +#: qt/manageprofiles/copylinkswidget.py:60 msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"すべてのシンボリックリンクは、それらが指す実際のファイルまたはディレクトリに置き換えられます。これによりバックアップサイズが増加し、同じファイルが複数回保存される可能性があります。" + +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "`rsync --copy-links` を使用します。" + +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "外部のみ" -#: ../../qt/app.py:1038 +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"バックアップソース外を指すリンクのみがファイルとしてコピーされます。これによりバックアップサイズが増加し、同じファイルが複数回保存される可能性があります。" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "`rsync --copy-unsafe-links` を使用します。" + +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "エディタおよびオフィスの一時ファイル" + +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacsのバックアップファイル" + +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacsの自動保存ファイル" + +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Vimのスワップファイル" + +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Officeの一時ファイル" + +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice およびその他の OpenDocument エディタはファイルをロックします" + +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "サムネイルと一時的な画像" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "GNU/Linuxおよびその他のUnix系OSにおけるサムネイルキャッシュ" + +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Windows上のサムネイルデータベース" + +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "MacOS上のメタデータディレクトリ" + +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "アプリケーション固有のロック" + +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discordアプリケーションロックファイル" + +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord セッションロックファイル" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Mozilla Firefox および Thunderbird のロックファイル" + +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "キャッシュと一時ディレクトリ" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "ユーザーアプリケーションキャッシュ" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "システム一時ディレクトリ" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "DebianベースのGNU/Linuxディストリビューション向けパッケージキャッシュ" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Flatpakアプリおよびランタイムリポジトリ" + +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "システム実行時ディレクトリ" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "カーネルとプロセス情報" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "デバイスおよびその他のハードウェア情報(sysfsインターフェース)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "デバイスノード" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "ランタイムシステムファイル" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "その他の非永続的" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "現在マウントされているファイルシステムのリスト" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "システムスワップファイル(仮想メモリ)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "GNOME仮想ファイルシステムのマウントポイント" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "回復されたファイルシステムオブジェクト" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "その他" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Microsoft Windows 上のメタデータ ディレクトリ" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "ユーザーごみ箱" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "システムバックアップファイル" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "提案を除外する" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "バックアップ除外項目に追加する、よく使用するアイテムを選択してください。" + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "デフォルト" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "事前定義された選択にリセット" + +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" +msgstr "スケジュール" + +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" +msgstr "日:" + +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" +msgstr "曜日:" + +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "時間:" + +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" +msgstr "時間:" -#: ../../qt/app.py:1072 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "毎正時後" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "分:" + +#: qt/manageprofiles/schedulewidget.py:93 msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "ドライブが接続されたら Back In Time を実行 (ただし X日に一回)。 sudo のパスワードプロンプトが表示されるでしょう。" -#: ../../qt/app.py:1083 -#, python-format +#: qt/manageprofiles/schedulewidget.py:98 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" -msgstr "" +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." +msgstr "Back In Time を繰り返し実行する。コンピュータを定期的に起動していない場合に便利です。" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" +msgstr "毎:" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "デバッグメッセージのログを有効化" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "デバッグレベルメッセージを 「--debug」 経由でシステムログに書き込む。" -#: ../../qt/app.py:1105 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" -msgstr "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "注意: これは非常に大量の出力を発生させるので、臨時の診断に使用してください。" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "現在のディスクの内容を閲覧" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "無効" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "スナップショット:%s" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "起動/再起動毎に" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "%s に作成したスナップショットを閲覧" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "毎{n}分ごと" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "毎{n}時間ごと" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "カスタム時間" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "毎日" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "繰り返し (anacron)" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "ドライブが接続されたとき(udev)" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "毎週" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "毎月" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "毎年" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "プロファイル:" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "時間" -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "フィルタ:" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "日" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "全て" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "週" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "エラー" +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "カ月" + +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." +msgstr "カスタム時間には、カンマ区切りの時間リスト(例:8,12,18,23)または */3 3時間ごとの定期バックアップのみを指定できます。" + +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "პირადი გასაღები:" + +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "" + +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" + +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH პროქსი" + +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "ჰოსტი:" + +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "პორტი:" + +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "მომხმარებელი:" + +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "ფაილების დამატება" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "საქაღალდეების დამატება" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "მინიშნებები" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "ფაილების გამორიცხვა" + +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "საქაღალდეების გამორიცხვა" + +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:116 +msgid "on local machine" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:130 +msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:204 +msgid "Preserve ACL" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:222 +msgid "Preserve extended attributes (xattr)" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format +msgid "" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "ნაგულისხმევი" + +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "გამორთულია" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "ჩართულია" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "რეჟიმი:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH-ის მორგება" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "ბილიკი:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "გასაღების ფაილი:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "პაროლი" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "დამატებით" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." +msgstr "" + +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "" + +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." +msgstr "" + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "" + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "" + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." +msgstr "" + +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" + +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "" + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format +msgid "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." +msgstr "" + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "ფაილების ჩასმა" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "საქაღალდეების ჩასმა" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "გაფრთხილებების ჩართვა" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "" + +#: qt/manageprofiles/tab_options.py:56 +msgid "" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." +msgstr "" + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "" + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." +msgstr "" + +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." +msgstr "" + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "ჟურნალის დონე:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "არცერთი" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "" + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "მალსახმობები" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "ადგილები" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "ფაილური სისტემა" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "მუშავდება…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "კონფიგურაციის შემოტანა" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "შემოტანა" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "ძებნა…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "ძებნა დასრულდა." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "" + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "" + +#: qt/snapshotsdialog.py:68 +msgid "Command:" +msgstr "ბრძანება:" + +#: qt/snapshotsdialog.py:72 +msgid "Parameters:" +msgstr "პარამეტრები:" + +#: qt/snapshotsdialog.py:77 +msgid "Use %1 and %2 for path parameters" +msgstr "" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "" + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "მარქაფები" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "" + +#: qt/snapshotsdialog.py:171 +msgid "Deep check (more accurate, but slow)" +msgstr "" + +#: qt/snapshotsdialog.py:192 +msgid "Delete" +msgstr "წაშლა" + +#: qt/snapshotsdialog.py:197 +msgid "Select All" +msgstr "ყველას მონიშვნა" + +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "შედარება" + +#: qt/snapshotsdialog.py:232 +msgid "Go To" +msgstr "გადასვლა" + +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "მორგება" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "" + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "" + +#: qt/statusbar.py:87 +msgid "" +"Back In Time is currently running with root privileges (full system access)" +msgstr "" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "დღეს" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "გუშინ" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "ეს კვირა" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "წინა კვირაში" + +#: qt/timeline.py:88 +msgid "This month" +msgstr "" + +#: qt/timeline.py:95 +msgid "Last month" +msgstr "" + +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" diff --git a/common/po/ko.po b/common/po/ko.po index 53e8ce11e..b578615ed 100644 --- a/common/po/ko.po +++ b/common/po/ko.po @@ -1,1662 +1,2550 @@ -# Korean translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-10-30 18:04+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Korean \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-03-10 15:41+0000\n" +"Last-Translator: darkcircle \n" +"Language-Team: Korean \n" +"Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" +"X-Generator: Weblate 5.10.2\n" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "프로파일 \"%s\"가 이미 존재합니다." +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "경고" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "마지막 프로파일은 제거할 수 없습니다." +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "메인 프로파일" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "비활성화됨" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "로컬 (EncFS 암호화)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "부팅/재부팅 할 때 마다" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS 암호화)" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "매 5분" +#: common/config.py:237 +msgid "Local" +msgstr "로컬" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "매 10분" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "로컬 암호화" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "30분마다" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "암호화" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "1시간마다" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "2시간마다" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH 개인 키" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "4시간마다" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "프로필: \"{name}\"" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "6시간마다" +#: common/config.py:301 +#, fuzzy +msgid "Backup directory is not valid." +msgstr "스냅샷 폴더가 올바르지 않습니다." -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "12시간마다" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "백업을 위해서는 적어도 하나의 디렉토리가 선택되어야 합니다." -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "디렉터리: {path}" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "매일" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "이 디렉터리는 백업 저장소 자체의 일부이므로 백업 대상에 넣어둘 수 없습니다." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "매주" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "매월" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "udev 일정이 {mode} 모드로 동작하지 않습니다" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "일" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "새로운 크론탭 작성에 실패했습니다." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "주" +#: common/config.py:1577 +#, fuzzy +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"크론탭(crontab) 명령어가 가능하지만 크론(Cron)이 작동하지 않습니다. 스케쥴 백업이 작동하지 않을 것입니다. 크론(Cron)이" +" 설치 되어있을 수도 있지만 활성화가 안되어있을 수 도 있습니다. 다음 명령어 \"systemctl enable cron\"를 시도하거나" +" 당신의 GNU/Linux 배포판에 도움을 요청해보세요." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "설정 저장에 실패했습니다" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "설정 불러오기에 실패했습니다" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "\"{name}\" 프로필이 이미 있습니다." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "마지막 프로필은 제거할 수 없습니다." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "'{command}' 명령을 마운트할 수 없습니다" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "암호화 디렉터리 설정을 찾을 수 없습니다." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "새 암호화 폴더를 만드시겠습니까?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "취소" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "년" +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "\"{user}\" 암호를 입력하십시오." -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "암호가 일치하지 않습니다." -#: ../../common/config.py:105 -msgid "Month(s)" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "스냅샷 생성하기" + +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "'{command}' 명령을 마운트할 수 없습니다" + +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "{mountpoint}(에)서 {mountprocess}을(를) 마운트 해제할 수 없습니다." + +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "{command}을(를) 찾을 수 없습니다. {installcommand} 명령으로 설치하십시오" + +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "{mntpoint} 마운트 지점이 비어 있지 않습니다." + +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "\"{profile}\" {mode} 프로파일의 암호를 입력하십시오:" + +#: common/schedule.py:238 +#, fuzzy, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" +"{profile_id} 프로필에 Udev 규칙을 설치할 수 없습니다. DBus 서비스 '{dbus_interface}'을(를) 사용할 수" +" 없습니다" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "{path}의 UUID를 찾을 수 없습니다" -#: ../../common/config.py:129 -msgid "Local" -msgstr "" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "실패" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "권한 복원" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "SSH 개인키(비공개키)" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "완료" -#: ../../common/config.py:131 -msgid "Local encrypted" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" msgstr "" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "암호화" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "배터리를 사용하는 동안 백업 연기" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" +#: common/snapshots.py:931 qt/app.py:377 +#, fuzzy +msgid "Can't find backup directory." +msgstr "스냅샷 디렉터리를 찾을 수 없습니다." -#: ../../common/config.py:135 -msgid "Default" -msgstr "기본값" +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "이동식 드라이브에 있다면 우선 연결하십시오." -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:938 +#, fuzzy, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "%s초 기다립니다." -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:1005 +#, fuzzy, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "{snapshot_id} 스냅샷을 생성하지 못했습니다." -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "잠시만 기다려주세요. 마무리 중…" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "디렉터리를 만들 수 없습니다." -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "구성 파일 저장 중…" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "권한 저장 중…" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:1377 +#, fuzzy, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "계속할 수 있는 남은 {snapshot_id} 스냅샷을 찾았습니다." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:1401 +#, fuzzy, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "마지막 실행에서 남은 {snapshot_id} 디렉터리 제거 중" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "디렉터리를 제거할 수 없습니다" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "성공" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "오류 때문에 일부만 전송했습니다" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "메인 프로파일" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "사라진 소스 파일로 인한 부분 전송('man rsync' 참조)" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "프로파일: \"%s\"" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync'가 종료 코드 {exit_code}로 종료되었습니다" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "스냅샷 폴더가 유효하지 않습니다." +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "자세한 내용은 'man rsync'를 참조하세요" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "최소한 하나의 백업할 폴더를 선택해야 합니다." +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "rsync 음수 종료 코드는 시그널 번호입니다. 'kill -l' 및 'man kill'을 참조하세요" + +#: common/snapshots.py:1566 +#, fuzzy +msgid "Nothing changed, no new backup necessary" +msgstr "아무것도 변경되지 않았으며 새 스냅샷이 필요하지 않습니다" + +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "{new_path} 경로 이름을 {path} 경로 이름으로 바꿀 수 없습니다." + +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "오래된 스냅샷을 제거할 규칙 적용" + +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "보호 정책 적용 중" + +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "최소 여유 공간을 유지하려고 합니다" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "백업 폴더를 포함할 수는 없습니다." +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "최소 {perc}개의 사용 가능한 아이노드를 유지하려고 합니다" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "백업 하위폴더를 포함할 수는 없습니다." +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "현재" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s는 폴더가 아닙니다." +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "{sshfs}을(를) 마운트할 수 없습니다" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent가 없습니다. 설치 여부를 확인하십시오." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format +#: common/sshtools.py:489 msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"%s에 쓰기를 할 수 없습니다.\n" -"쓰기 권한이 있는지를 확인하세요." +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "SSH 개인 키를 잠금 해제할 수 없습니다. 잘못된 암호거나 cron 암호를 사용할 수 없습니다." + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "원격 경로가 존재하지만 디렉터리가 아닙니다." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "원격 경로에 쓸 수 없습니다." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "원격 경로를 실행할 수 없습니다." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "원격 경로를 생성할 수 없습니다." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "원격 호스트 {host}은(는) {command}을(를) 지원하지 않습니다" + +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "호스트 {host}의 확인 명령이 알 수 없는 오류를 반환했습니다" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "원격 호스트 {host}은(는) 하드링크를 지원하지 않습니다" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "\"{pubkey}\" 공개 SSH 키를 \"{host}\" 원격 호스트에 복사합니다." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "\"{user}\" 암호를 입력하십시오." + +#: common/tools.py:382 +#, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." +msgstr "{path}의 대상 파일 시스템은 유닉스 방식 파일 시스템과 호환성이 없다고 알려진 NTFS 형식으로 초기화했습니다." + +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path}은(는) 적절한 디렉터리가 아닙니다." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "해당 디렉토리 생성에 실패:" + +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "쓰기 권한이 제한되어있습니다." -#: ../../common/config.py:402 -#, python-format +#: common/tools.py:471 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"{path}의 대상 파일 시스템은 하드 링크를 지원하지 않는 FAT 형식으로 포맷했습니다. GNU/Linux 자체 파일 시스템을 " +"사용하십시오." -#: ../../common/config.py:407 -#, python-format +#: common/tools.py:482 +#, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"{path}의 대상 파일 시스템은 SMB 형식 공유 마운트입니다. 원격 SMB 서버가 심볼릭 링크를 지원하는지 확인하거나 " +"\"{expertOptions}\"에서 \"{copyLinks}\"를 활성화하십시오." -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 +#: common/tools.py:486 msgid "Copy links (dereference symbolic links)" -msgstr "링크 복사(심볼릭 링크와는 다름)" +msgstr "링크 복사 (심볼릭 링크 역참조)" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 +#: common/tools.py:487 msgid "Expert Options" -msgstr "고급 설정" +msgstr "전문가 설정" -#: ../../common/config.py:413 -#, python-format +#: common/tools.py:491 +#, python-brace-format msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"{path}의 대상 파일 시스템은 SSHFS 공유 마운트 입니다. SSHFS는 하드 링크를 지원하지 않습니다. 대신 \"SSH\" 모드를" +" 사용하십시오." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"crontab를 찾을 수 없습니다.\n" -"cron이 설치되어 있습니까?\n" -"설치되지 않았다면 모든 자동 백업을 실행불가 상태로 해야 합니다." +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "디렉토리에서 파일 생성에 실패:" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: qt/aboutdlg.py:50 +#, fuzzy +msgid "About Back In Time" +msgstr "Back In &Time" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" +#: qt/aboutdlg.py:88 +msgid "Copyright:" msgstr "" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: qt/aboutdlg.py:92 +#, fuzzy +msgid "Authors:" +msgstr "저자" + +#: qt/aboutdlg.py:96 +#, fuzzy +msgid "Translators:" +msgstr "번역" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." msgstr "" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." +#: qt/aboutdlg.py:115 +msgid "this link" msgstr "" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "취소" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "프로젝트 사이트" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "비밀번호 다시 입력" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "사용자 메뉴얼" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "비밀번호 불일치" +#: qt/aboutdlg.py:247 qt/app.py:546 +#, fuzzy +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "사용자 메뉴얼을 브라우저에서 열기(로컬이 안되면 온라인으로)" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" msgstr "" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "스냅샷 찍기" +#: qt/app.py:332 +#, fuzzy, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." +msgstr "{app_name}을 어떤 설정 없이 처음 실행하는 것 같습니다." -#: ../../common/mount.py:415 -#, python-format +#: qt/app.py:337 +#, fuzzy msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" +"Import an existing configuration from a backup location or another computer?" +msgstr "(백업 대상 디렉터리 또는 다른 컴퓨터에서) 기존 설정을 가져오시겠습니까?" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" +#: qt/app.py:379 +msgid "Then press OK." msgstr "" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" +#: qt/app.py:483 +msgid "Create a backup" msgstr "" -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "파일 변경 감지를 위해 수정 시간 및 크기를 사용합니다." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "마운트 위치(폴더) %s가 비어 있지 않습니다." +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "체크섬을 포함한 스냅샷 생성하기" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "파일 변경 감지에 체크섬을 사용합니다." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +#, fuzzy +msgid "Pause backup process" +msgstr "스냅샷 생성 일시 중지" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +#, fuzzy +msgid "Resume backup process" +msgstr "스냅샷 생성 재시작" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "실패" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +#, fuzzy +msgid "Stop backup process" +msgstr "스냅샷 생성 중지" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" +#: qt/app.py:504 +#, fuzzy +msgid "Refresh backup list" +msgstr "스냅샷 목록 새로 고침" + +#: qt/app.py:508 +msgid "Name backup" msgstr "" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "완료" +#: qt/app.py:512 +#, fuzzy +msgid "Remove backup" +msgstr "스냅샷 제거" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "마지막 로그 보기" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "스냅샷 폴더를 찾을 수 없습니다." +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "백업을 위해서는 적어도 하나의 디렉토리가 선택되어야 합니다." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "%s초 기다리는 중" +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" +msgstr "마지막 로그 보기" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "%s 스냅샷을 가지지 못했습니다 !!!" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "마치는 중" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "프로필 관리…" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "폴더(%s)를 생성할 수 없음" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "user-callback 편집" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "설정 파일 저장 ..." +#: qt/app.py:532 +msgid "Shutdown" +msgstr "끄기" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "권한을 저장 ..." +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." +msgstr "스냅샷이 끝난 후 시스템 종료하기." -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/app.py:536 +msgid "Setup language…" +msgstr "언어 설정…" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/app.py:540 +msgid "Exit" +msgstr "끝내기" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "맨 페이지: Back In Time" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "폴더: %s를 제거할 수 없습니다." +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Back In Time (backintime) 맨 페이지를 표시합니다" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "스냅샷 찍기" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "맨 페이지: 프로필 설정 파일" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "프로파일 설정(backintime-config)에 대한 man 페이지 열기" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "브라우저에서 Back In Time 웹사이트 열기" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "똑똑한 제거" +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "변경 내역" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "오래된 스냅샷을 제거합니다." +#: qt/app.py:570 +#, fuzzy +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "사용자 메뉴얼을 브라우저에서 열기(로컬이 안되면 온라인으로)" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "최소 여유 공간을 유지합니다." +#: qt/app.py:573 +msgid "FAQ" +msgstr "자주 묻는 질문" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "브라우저에서 자주 묻는 질문들 (FAQ) 열기" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "질문하기" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "현재" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "버그 신고" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/app.py:584 +msgid "Translation" +msgstr "번역" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "번역 참여에 대한 메세지를 다시 보기." -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Transition 암호화 (EncFS)" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "EncFS 제거에 대한 메시지를 다시 보기." -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" +#: qt/app.py:599 +msgid "About" +msgstr "정보" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" -"원격 패스가 있지만 디텍토리가 아닙니다.\n" -" %s" +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "복원" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" -"다음 원격 패스에 쓸 수 없습니다.\n" -" %s" +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "선택한 파일이나 디렉터리를 원래 대상으로 복원합니다." -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "다음 위치에 복원 …" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "선택한 파일 또는 디렉터리를 새로운 대상으로 복원합니다." + +#: qt/app.py:615 +#, fuzzy msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" -"다음 원격 패스를 실행 할 수 없습니다.\n" -" %s" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "현재 표시한 디렉터리와 해당 디렉터리의 모든 내용을 원래 대상으로 복원합니다." -#: ../../common/sshtools.py:474 -#, python-format +#: qt/app.py:621 +#, fuzzy msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" -"다음 원격 패스를 만들 수 없습니다.\n" -" %s" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "현재 나타난 디렉터리와 해당 디렉터리의 모든 내용을 새 대상으로 복원합니다." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "호스트가 다운 되었거나 잘못된 주소입니다. %s ping 실패" +#: qt/app.py:624 +msgid "Up" +msgstr "위로" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "숨겨진 파일 표시" -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:630 +#, fuzzy +msgid "Compare backups…" +msgstr "스냅샷 비교…" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "원격호스트 %s가 하드링크를 지원하지 않습니다." +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "배포 후보" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "배포 후보에 대한 메시지 다시 보기." -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "백업" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "복원" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "도움말" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "스냅샷 이름" - -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "스냅샷 제거" - -#: ../../qt/app.py:129 -msgid "View Snapshot Log" +#: qt/app.py:784 +msgid "Light icon" msgstr "" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "설정" +#: qt/app.py:808 +msgid "Icons only" +msgstr "아이콘만" -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "끄기" +#: qt/app.py:811 +msgid "Text only" +msgstr "글자만" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "아이콘 밑 글자" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "끝내기" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "아이콘 옆 글자" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "도움말" +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." +msgstr "" +"현재 선택한 스냅샷에\n" +"이 디렉터리가 없습니다." -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"현재 선택한 스냅샷에\n" +"이 디렉터리가 없습니다." -#: ../../qt/app.py:163 -msgid "Website" -msgstr "웹사이트" +#: qt/app.py:995 +#, fuzzy +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." +msgstr "이 창을 닫으면 스냅샷을 마쳤을 때 Back In Time에서 시스템을 끌 수 없습니다." -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" +#: qt/app.py:998 +msgid "Close the window anyway?" msgstr "" -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "완료했습니다. 백업이 필요하지 않습니다" -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "" +#: qt/app.py:1260 +msgid "Working:" +msgstr "진행 중:" -#: ../../qt/app.py:171 -msgid "Report a bug" -msgstr "" +#: qt/app.py:1267 +msgid "Working" +msgstr "진행 중" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "정보" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "오류" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "위로" +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "전송:" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "숨김 파일 표시" +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "속도:" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "복원" +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "남은시간:" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "백업" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "다음 위치에 복원" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "{path} 복원" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "{path}를 다음 위치에 복원 …" -#: ../../qt/app.py:234 +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"안녕하세요\n" +"지금까지 여러분은 {language} 언어로 Back In Time을 몇 번 사용해 보셨습니다.\n" +"설치된 Back In Time 버전의 {language} 번역이 {perc} 가량 끝났습니다. 여러분의 기술 전문 지식 수준에 관계없이 귀하는 번역에 기여하여 Back In Time 자체에 기여할 수 있습니다.\n" +"기여하고 싶다면 {translation_platform_url}를 방문하세요. 추가 지원 및 질문이 있다면 {back_in_time_project_website}를 방문하세요.\n" +"불편을 끼쳐드려 죄송하며 이 메시지는 다시 표시되지 않습니다. 이 대화 상자는 도움말 메뉴를 통해 언제든지 살펴보실 수 있습니다.\n" +"여러분의 Back In Time 팀" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "번역 플랫폼" + +#: qt/app.py:1658 +msgid "Website" +msgstr "웹사이트" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "귀하의 번역" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:1709 +#, fuzzy, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Mastodon에서 보기: {link_and_label}" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "스냅샷" +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "{link_and_label} 메일링 리스트" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:1718 +#, fuzzy, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "{link_and_label} 메일링 리스트" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "프로젝트 웹사이트의 {link_and_label}." -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "바로 가기" +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "문제 보고 열기" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "대신, 다른 채널을 활용하여 연락할 수 있습니다." -#: ../../qt/app.py:398 -msgid "Add to Include" +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"이번 Back In Time 버전은 출시 후보작으로 다음 정식 출시 버전의 준비를 위한 안정화 시험이 주 목적입니다.\n" +"사용자 데이터 또는 원격 데이터는 수집하지 않습니다. 그러나, Back In Time 팀에서는 출시 후보작의 사용 여부와 출시 이전 버전의 계속적 제공 가치 여부를 확인하고 싶습니다.\n" +"따라서, 팀에서는 어떤 문제를 경험해보지 못했다 하더라도 이 버전의 시험 여부에 대한 간단한 고견을 듣고자 합니다. 몇 분동안의 간단한 시험 실행만으로도 저희에겐 큰 도움이 됩니다.\n" +"다음과 같은 방식으로 연락할 수 있습니다:\n" +"{contact_list}\n" +"이 버전에서, 이 메시지는 더이상 나타나지 않지만 도움말 메뉴에서 언제든 살펴보실 수 있습니다.\n" +"Back In Time 개선을 지원해주시는 여러분께 감사합니다!\n" +"여러분의 Back In Time 팀" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" +#: qt/app.py:1841 +msgid "Proceed with the backup?" msgstr "" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -"스냅샷 폴더를 찾을 수 없습니다.\n" -"만일 이동식 드라이브에 저장되었다면 연결 하신 후에 확인을 누르십시오." -#: ../../qt/app.py:527 +#: qt/app.py:1875 +#, fuzzy, python-brace-format msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" -msgstr "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." +msgstr "{BOLD}경고{BOLDEND}: 파일 시스템 루트에서 파일을 삭제하면 전체 시스템이 망가질 수 있습니다." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "진행중:" - -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "완료. 백업이 필요하지 않습니다." +#: qt/app.py:2107 +#, fuzzy +msgid "Backup name" +msgstr "백업" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "오류:" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" -msgstr "" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "언어 설정은 Back In Time을 다시 시작한 후에만 적용됩니다." -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" msgstr "" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "전역" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "루트" - -#: ../../qt/app.py:796 -msgid "Home" -msgstr "홈" - -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "백업 폴더" +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "이름이 붙은 스냅샷 유지." -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -"정말 스냅샷을 제거하시겠습니까?\n" -"%s" -#: ../../qt/app.py:1025 -#, python-format +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "질문" + +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"로컬 요소를 덮어쓰거나 제거하기 전에\n" +"뒤에 오는 {suffix}를 사용하여 백업 복사본을 만듭니다." -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, fuzzy, python-brace-format msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"파일 최신 버전은 복원하기 전 {suffix}를 붙여 이름을 바꿉니다. 해당 파일이 더이상 필요하지 않으면 다음 명령으로 제거할 수 " +"있습니다:" -#: ../../qt/app.py:1038 +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"백업 대상 위치에 없거나\n" +"해당 위치보다 최신인 항목만 복원합니다.\n" +"\"rsync --update\" 옵션을 사용하십시오." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "원본 디렉터리에서 최신 요소를 제거합니다." -#: ../../qt/app.py:1072 +#: qt/confirmrestoredialog.py:135 +#, fuzzy msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." msgstr "" +"선택한 파일 또는 디렉터리를 원래 대상으로 복원하고 스냅샷에 없는 파일 또는 디렉터리는 삭제합니다. 스냅샷을 취할 때 제외한 파일 및 " +"디렉터리도 삭제하므로 취급에 신중을 기하십시오." -#: ../../qt/app.py:1083 -#, python-format -msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" -msgstr "" +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "이 항목을 새 디렉터리에 복원하시겠습니까?" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "정말로 이 항목을 복원하시겠습니까?" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" msgstr "" -#: ../../qt/app.py:1101 +#: qt/editusercallback.py:105 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" -#: ../../qt/app.py:1105 -msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" -msgstr "" +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "파일 및 디렉터리 포함" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "현재 디스크의 내용을 봅니다." +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "포함할 항목 추가" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "스냅샷: %s" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "제외할 항목 추가" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "언어 설정" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "{percent} 번역됨" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "시스템 기본값" + +#: qt/languagedialog.py:142 +#, fuzzy +msgid "Use operating system's language." +msgstr "운영 체제의 언어를 사용합니다." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "마지막 로그 보기" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "마지막 로그 보기" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "%s에 만들어진 스냅샷을 봅니다." +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "프로필:" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "%s 을/를 복원" +#: qt/logviewdialog.py:86 +#, fuzzy +msgid "Backups:" +msgstr "백업" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "다음 위치에 %s 을/를 복원" +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "필터:" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] 오류, [I] 정보, [C] 변경" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "경로 디코딩" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "모두" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "변경" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "오류" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "프로파일:" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "정보" -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync 전송 실패 (시험용)" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "프로필 관리" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "편집" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "추가" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "삭제" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "일반(&G)" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "포함(&I)" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "제외(&E)" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "제거(&R) 및 보유" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "옵션(&O)" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "고급 설정" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "설정 복구" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "새로운 프로파일" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "프로파일명을 변경" + +#: qt/manageprofiles/__init__.py:215 +#, fuzzy, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "정말 \"{name}\" 프로파일을 삭제하시겠습니까?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." msgstr "" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." msgstr "" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" msgstr "" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "동작 중..." +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "스냅샷 생성 일시 중지" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "오늘" +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "제외 파일" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "어제" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "어번 주" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "지난 주" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "편집" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "일반" +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "백업 디렉터리" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "디렉터리를 제거할 수 없습니다" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "스냅샷을 저장할 위치" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "숨겨진 파일 표시" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "SSH 설정" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "스냅샷 생성 중지" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "제외 디렉터리" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "고급 설정" +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "기본값" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "일정" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "" +msgstr "일:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "" +msgstr "요일:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" msgstr "시간:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" -msgstr "" +msgstr "시간:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "한시간 후" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "분:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:93 +#, fuzzy +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "드라이브를 연결하면 Back In Time을 실행합니다(매 X일에 한번씩). sudo 암호 입력이 필요합니다." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." -msgstr "" +msgstr "반복적으로 Back In Time을 실행합니다. 컴퓨터를 비정기적으로 사용할 때 쓸만합니다." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "" +msgstr "매:" -#: ../../qt/settingsdialog.py:382 -msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "디버깅 메시지 기록 활성" -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "포함" +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "디버깅 수준 메시지를 \"--debug\" 옵션으로 시스템 로그에 기록합니다." -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:120 +msgid "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "위험: 엄청난 양의 출력 내용을 만들어내므로 진단 목적으로 잠시 동안만 사용하십시오." -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "파일 추가" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "비활성화됨" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "폴더 추가" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "부팅/재부팅 할 때 마다" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "제외" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "매 {n}분 마다" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "{n}시간마다" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "시간 주기 설정" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "매일" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "반복 (아나크론)" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "드라이브를 연결했을 때(udev)" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "매주" -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "매월" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "자동 제거" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "매년" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "날짜가 다음 보다 오래된 경우:" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "시간" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "여유 공간이 다음보다 작은 경우:" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "일" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "주" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "월" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"개별 설정 시간은 쉼표로 구분한 시간 값(예: 8, 12, 18, 23)이거나 3시간당 주기 백업일 경우 */3 과 같은 형식의 " +"값입니다." -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Senesni nei:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Pasirinkti esamą privataus rakto failą iš kažkur kitur." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Jei laisvos vietos mažiau nei:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Sukurti naują SSH raktą be slaptafrazės." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Visas kelias: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Privatus raktas:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Naudoti sistemos SSH konfigūraciją" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Palieka rakto failą nepasirinktą. SSH ryšiai remsis sistemos esama kliento " +"programos konfigūracija (pavyzdžiui, ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH įgaliotasis serveris" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +#, fuzzy +msgid "Host:" +msgstr "Pagrindinis kompiuteris:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Prievadas:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Naudotojas:" + +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Nešalinti momentinių kopijų, turinčių pavadinimus" +#: qt/manageprofiles/tab_exclude.py:85 +#, fuzzy +msgid "Exclude patterns, files or directories" +msgstr "Išskirkite šablonus, failus ar aplankus" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Parinktys" +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Išskirti šabloną" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Įgalinti pranešimus" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Pridėti failų" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Pridėti katalogų" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "Klausimas" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -#: ../../qt/settingsdialog.py:625 -msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Išskirti failus, didesnius nei:" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:138 +#, fuzzy, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Išskirti failus, didesnius, nei: " -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:140 +#, fuzzy +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Išskirkite failus, didesnius nei %(prefix)s.\n" +"Išjungus „Visas rsync režimas“, tai turės įtakos tik naujiems failams\n" +"nes rsync tai yra perdavimo parinktis, o ne išimtis.\n" +"Taigi dideli failai, kurių atsarginės kopijos buvo sukurtos anksčiau, liks momentinėse nuotraukose\n" +"net jei jie pasikeitė." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Išskirti šabloną" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Išskirti šabloną" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:658 -msgid "None" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" msgstr "" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Pakeitimai ir klaidos" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Išskirti failą" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Išskirti katalogą" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"Šios parinktys yra skirtos išplėstinėms konfigūracijoms. Modifikuokite jas " +"tik tuo atveju, jei visiškai suprantate jų reikšmę." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Paleisti „rsync“ su „{cmd}“:" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +#, fuzzy msgid "as cron job" -msgstr "" +msgstr "kaip cron job" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +#, fuzzy msgid "on remote host" -msgstr "" +msgstr "nuotoliniame pagrindiniame kompiuteryje" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +#, fuzzy +msgid "when taking a manual backup" +msgstr "darant momentinę kopiją rankiniu būdu" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Norėdami įjungti šią parinktį, įsidiekite „nocache“." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" - -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "vietiniame kompiuteryje" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 +#, fuzzy msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Nukreipkite stdout į /dev/null cronjobs." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 +#, fuzzy msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Nukreipkite stderr į /dev/null cronjobs." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/sek" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +#, fuzzy +msgid "Limit rsync bandwidth usage:" +msgstr "Apriboti rsync pralaidumo naudojimą" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "" +msgstr "Išsaugoti ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "" +msgstr "Išsaugoti išplėstinius atributus (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" msgstr "" -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" +#: qt/manageprofiles/tab_expert_options.py:267 +#, fuzzy, python-brace-format +msgid "Options must be quoted e.g. {example}." msgstr "" +"Įklijuoti papildomas parinktis į rsyncOptions turi būti cituojamos pvz. " +"{example}." -#: ../../qt/settingsdialog.py:839 -msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "Įdėti papildomas parinktis, skirtas rsync" -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." msgstr "" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:287 +#, fuzzy, python-brace-format msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." +msgstr "" +"Priešdėlis paleisti prieš kiekvieną komandą nuotoliniame pagrindiniame kompiuteryje.\n" +"Kintamieji turi būti pakeisti naudojant \\$FOO.\n" +"Tai neliečia rsync. Taigi, norėdami pridėti priešdėlį\n" +"rsync naudokite „%(cbRsyncOptions)s“ su\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" -msgstr "" -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 +#: qt/manageprofiles/tab_expert_options.py:293 msgid "default" -msgstr "" +msgstr "pagal numatymą" + +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Pridėti SSH komandoms priešdėlį" -#: ../../qt/settingsdialog.py:870 +#: qt/manageprofiles/tab_expert_options.py:308 +#, fuzzy msgid "Check if remote host is online" +msgstr "Patikrinkite, ar nuotolinis kompiuteris yra prisijungęs" + +#: qt/manageprofiles/tab_expert_options.py:311 +#, fuzzy +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Įspėjimas: jei išjungta ir nuotolinis kompiuteris\n" +"nepasiekiamas, tai gali sukelti \n" +"keistas klaidas." -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:315 +#, fuzzy +msgid "Check if remote host supports all necessary commands." +msgstr "" +"Patikrinkite, ar nuotolinis kompiuteris palaiko visas reikalingas komandas" + +#: qt/manageprofiles/tab_expert_options.py:318 +#, fuzzy msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Įspėjimas: jei išjungta ir nuotolinis kompiuteris\n" +"nepalaiko visų reikalingų komandų,\n" +"tai gali sukelti keistų klaidų." + +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(pagal numatymą: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "išjungta" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "įjungta" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Veiksena:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Kur įrašyti atsargines kopijas" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH nustatymai" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Kelias:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Rakto failas:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Slaptažodis" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Įrašyti slaptažodį į raktinę" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:210 +#, fuzzy +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Išsaugoti Cron slaptažodį (Saugumo spraga: root gali matyti slaptažodį)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Išplėstiniai" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Visas atsarginės kopijos kelias:" + +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." msgstr "" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "EncFS slaptažodžiai nesutampa." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." +msgstr "" + +#: qt/manageprofiles/tab_general.py:560 +#, fuzzy +msgid "Proceed with copying the SSH key?" +msgstr "Šalinti šią atsarginę kopiją?" + +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." +msgstr "" +"Nepavyko nukopijuoti viešo SSH rakto. Taip gali būti dėl ryšio ar leidimų " +"problemos." + +#: qt/manageprofiles/tab_general.py:606 +#, fuzzy, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "" +"{host} autentiškumo nustatyti nepavyko.\n" "\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"{keytype} rakto kontrolinis kodas yra:" + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} rakto kontrolinis kodas yra:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "Patikrinkite šį kontrolinį kodą. Ar pridėti jį į „known_hosts“ failą?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Ar tikrai pakeisti atsarginės kopijos katalogą?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Neteisingas failas: Nėra privatus SSH raktas" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"Pasirinktas failas ({path}) yra viešas SSH raktas. Vietoj jo, pasirinkite " +"atitinkamą privataus rakto failą (be „.pub“)." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" +"Failas {path} jau yra. Negalima sukurti naujo SSH raktu tokiu pavadinimu." -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Nepavyko sukurti naujo SSH rakto ties {path}." + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Įtraukti failus bei katalogus" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"„{path}“ yra simbolinė nuoroda. Susieto paskirties objekto atsarginė kopija " +"nebus padaryta tol, kol jis taip pat nebus įtrauktas." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Ar vietoj simbolinės nuorodos norite įtraukti paskirties objektą?" + +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Įtraukti failą" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Įtraukti katalogą" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Įjungti pranešimus" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Išjungti atsargines kopijas, kai naudojama akumuliatoriaus energija" + +#: qt/manageprofiles/tab_options.py:49 +#, fuzzy +msgid "Power status not available from system" +msgstr "Maitinimo būsena nepasiekiama iš sistemos" + +#: qt/manageprofiles/tab_options.py:52 +#, fuzzy +msgid "Run only one backup at a time" +msgstr "Vienu metu paleisti tik vieną momentinę kopiją" + +#: qt/manageprofiles/tab_options.py:56 +#, fuzzy msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Kitos momentinės kopijos bus blokuojamos, kol bus padaryta dabartinė momentinė kopija.\n" +"Tai bendrasis variantas. Taigi tai turės įtakos visiems šio vartotojo profiliams.\n" +"Bet jūs turite tai suaktyvinti ir visiems kitiems vartotojams." + +#: qt/manageprofiles/tab_options.py:63 +#, fuzzy +msgid "Backup replaced files on restore" +msgstr "Atkūrimo metu sukurkite pakeistų failų atsarginę kopiją" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Tęsti, kai yra klaidų (palikti neužbaigtas atsargines kopijas)" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Pakeitimų aptikimui naudoti kontrolines sumas" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." msgstr "" +"Sukurti naują atsarginę kopiją, nepaisant to, ar buvo pakeitimų, ar ne." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Įspėti, jei laisvos vietos diske tampa mažiau nei" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Rodo įspėjimą, kai laisvos vietos atsarginės kopijos paskirties diske yra " +"mažiau nei nurodyta reikšmė." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Žurnalo lygis:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nėra" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "naudotojo vadovą" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, fuzzy, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Šios taisyklės yra apdorojamos nuo viršaus į apačią. Vėlesnės taisyklės " +"nustelbia ankstesnes. Norėdami gauti daugiau informacijos ir pavyzdžių, " +"žiūrėkite {manual}." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Išskirti šabloną" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Atverti naršyklėje naudotojo vadovą." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Išskirti failą" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Palikti paskiausią atsarginę kopiją." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Išskirti aplanką" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Įtraukti failą" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Ta elgsena negali būti pakeista." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Palikti atsargines kopijas, turinčias pavadinimus." + +#: qt/manageprofiles/tab_remove_retention.py:258 +#, fuzzy msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Atsarginės kopijos, kurioms buvo suteiktas pavadinimas, kartu su įprastomis " +"laiko žymomis, bus bet kokiomis sąlygomis iš" + +#: qt/manageprofiles/tab_remove_retention.py:273 +#, fuzzy +msgid "Year(s)" +msgstr "Metai (-ų)" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Įtraukti aplanką" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Šalinti atsargines kopijas, kurios senesnės nei" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." msgstr "" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12 mėnesių laikotarpiai. Dabartinio mėnesio yra nepaisoma." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/manageprofiles/tab_remove_retention.py:310 +#, fuzzy +msgid "Run in background on remote host." +msgstr "Vykdykite nuotolinio pagrindinio kompiuterio fone." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dienos skaičiuojamos, pradedant nuo šiandien." + +#: qt/manageprofiles/tab_remove_retention.py:322 +#, fuzzy +msgid "Keep all backups for the last" +msgstr "Išsaugokite visas momentines kopijas paskutinei" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +#, fuzzy +msgid "day(s)." +msgstr "Diena(os)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +#, fuzzy +msgid "Keep the last backup for each day for the last" +msgstr "Paskutinį kartą išsaugokite vieną momentinę kpiją per dieną" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:347 +#, fuzzy +msgid "Keep the last backup for each week for the last" +msgstr "Paskutinį kartą išsaugokite vieną momentinę kopiją per savaitę" + +#: qt/manageprofiles/tab_remove_retention.py:352 +#, fuzzy +msgid "week(s)." +msgstr "Savaitė(s)." + +#: qt/manageprofiles/tab_remove_retention.py:357 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The months are counted as calendar months starting with the current month." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:360 +#, fuzzy +msgid "Keep the last backup for each month for the last" +msgstr "Paskutinį kartą išsaugokite vieną momentinę kopiją per mėnesį" + +#: qt/manageprofiles/tab_remove_retention.py:365 +#, fuzzy +msgid "month(s)." +msgstr "Mėnesis(iai)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "" +"Metai yra skaičiuojami kaip kalendoriniai metai, pradedant dabartiniais " +"metais." + +#: qt/manageprofiles/tab_remove_retention.py:372 +#, fuzzy +msgid "Keep the last backup for each year for" +msgstr "Išsaugokite visas momentines kopijas paskutinei" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "" + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "…laisvos vietos yra mažiau nei" + +#: qt/manageprofiles/tab_remove_retention.py:394 +#, fuzzy +msgid "… the free inodes are less than" +msgstr "Jei laisvų inodų mažiau nei" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Šalinti seniausią atsarginę kopiją, jei…" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/placeswidget.py:51 +#, fuzzy +msgid "Shortcuts" +msgstr "Nuorodos" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Vietos" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Failų sistema" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Atsarginių kopijų katalogai" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profilis: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profilis: {profile_name}" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Rodyti paskutinį žurnalą" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Paleisti {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Dirbama…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" msgstr "" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importuoti konfigūraciją" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Pasikeitimų parinktys" +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importuoti" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Ieškoma…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" +"Pasirinkite atsarginės kopijos katalogą, iš kurio turėtų būti importuotas " +"konfigūracijos failas. Kelias gali atrodyti taip: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" +"Jei katalogo vieta yra išoriniame ar nuotoliniame diske, jis privalo būti iš" +" anksto prijungtas rankiniu būdu." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Peržiūrėti dar kartą" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Rodyti paslėptus katalogus" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Rodyti/slėpti paslėptus katalogus (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Nepavyko sukurti failo šiame kataloge:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Paieška užbaigta." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Rodyti visą žurnalą" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Atvirkštinis skaičiavimas iki išjungimo" + +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Išjungti sistemą, kai atsarginės kopijos darymas bus užbaigtas." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Atsisakyti išjungimo" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Išjungti dabar" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Sistema išsijungs po {n} sekundės." +msgstr[1] "Sistema išsijungs po {n} sekundžių." +msgstr[2] "Sistema išsijungs po {n} sekundžių." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Atsarginių kopijų palyginimo parinktys" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Komanda:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametrai:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 +#, fuzzy msgid "Use %1 and %2 for path parameters" msgstr "naudoti %1 ir %2 kaip kelio parametrus" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." msgstr "" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"Šioje sistemoje nepavyko rasti „{cmd}“ komandos. Pabandykite kažką kito arba" +" paspauskite „Atsisakyti“." -#: ../../qt/snapshotsdialog.py:128 -msgid "Deep check (more accurate, but slow)" +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Atsarginės kopijos" + +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Tik skirti skirtingus momentinius vaizdus" + +#: qt/snapshotsdialog.py:159 +#, fuzzy +msgid "List only backups that are equal to:" +msgstr "Išvardykite tik tokias momentines nuotraukas kaip: " + +#: qt/snapshotsdialog.py:171 +msgid "Deep check (more accurate, but slow)" +msgstr "Gilus tikrinimas (tikslesnis, bet lėtesnis)" + +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Ištrinti" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Žymėti viską" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Pasikeitimai" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Palyginti" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Eiti į" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Jūs negalite palyginti momentinės kopijos su ja pačia" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Parinktys" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Komanda nerasta: %s" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Neįmanoma palyginti atsarginę kopiją su ja pačia, nes palyginimas būtų " +"nereikalingas." -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" +"Ar tikrai ištrinti {file_or_dir}, esantį atsarginėje kopijoje {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Ar tikrai norite ištrinti „{file}“ iš {count} momentinių nuotraukų?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "ĮSPĖJIMAS: To negalima bus panaikinti." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Išskirti {path} iš atsarginių kopijų ateityje?" + +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Šaknis" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Šiandien" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Vakar" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Šią savaitę" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Praeitą savaitę" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Paskutinis tikrinimas {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Tai NĖRA atsarginė kopija, o tiesioginis vietinių failų rodinys." diff --git a/common/po/messages.pot b/common/po/messages.pot index 07acdf494..20539fd99 100644 --- a/common/po/messages.pot +++ b/common/po/messages.pot @@ -1,1650 +1,2398 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. +# © 2008 Back In Time Team # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. #, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-04-27 15:27+0200\n" +"Project-Id-Version: \"Back In Time\" \"2.0.0-dev.5d7ff833\"\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" msgstr "" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" msgstr "" -#: ../../common/config.py:77 -msgid "Disabled" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" msgstr "" -#: ../../common/config.py:78 -msgid "At every boot/reboot" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" msgstr "" -#: ../../common/config.py:79 -msgid "Every 5 minutes" +#: common/config.py:237 +msgid "Local" msgstr "" -#: ../../common/config.py:80 -msgid "Every 10 minutes" +#: common/config.py:240 +msgid "Local encrypted" msgstr "" -#: ../../common/config.py:81 -msgid "Every 30 minutes" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" msgstr "" -#: ../../common/config.py:82 -msgid "Every hour" +#: common/config.py:245 +msgid "SSH" msgstr "" -#: ../../common/config.py:83 -msgid "Every 2 hours" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" msgstr "" -#: ../../common/config.py:84 -msgid "Every 4 hours" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" msgstr "" -#: ../../common/config.py:85 -msgid "Every 6 hours" +#: common/config.py:301 +msgid "Backup directory is not valid." msgstr "" -#: ../../common/config.py:86 -msgid "Every 12 hours" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." msgstr "" -#: ../../common/config.py:87 -msgid "Custom Hours" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" msgstr "" -#: ../../common/config.py:88 -msgid "Every Day" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" -#: ../../common/config.py:91 -msgid "Every Week" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" msgstr "" -#: ../../common/config.py:92 -msgid "Every Month" +#: common/config.py:1569 +msgid "Failed to write new crontab." msgstr "" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." msgstr "" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" +#: common/configfile.py:101 +msgid "Failed to save config" msgstr "" -#: ../../common/config.py:98 -msgid "Year(s)" +#: common/configfile.py:137 +msgid "Failed to load config" msgstr "" -#: ../../common/config.py:102 -msgid "Hour(s)" +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." msgstr "" -#: ../../common/config.py:105 -msgid "Month(s)" +#: common/configfile.py:725 +msgid "The last profile cannot be removed." msgstr "" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" msgstr "" -#: ../../common/config.py:129 -msgid "Local" +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." msgstr "" -#: ../../common/config.py:130 -msgid "SSH" +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" msgstr "" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1629 -msgid "SSH private key" +#: common/encfstools.py:204 +msgid "Cancel" msgstr "" -#: ../../common/config.py:131 -msgid "Local encrypted" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." msgstr "" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." msgstr "" -#: ../../common/config.py:132 -msgid "SSH encrypted" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" msgstr "" -#: ../../common/config.py:135 -msgid "Default" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" msgstr "" -#: ../../common/config.py:136 -msgid "AES128-CTR" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." msgstr "" -#: ../../common/config.py:137 -msgid "AES192-CTR" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" -#: ../../common/config.py:138 -msgid "AES256-CTR" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." msgstr "" -#: ../../common/config.py:139 -msgid "ARCFOUR256" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" -#: ../../common/config.py:140 -msgid "ARCFOUR128" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" -#: ../../common/config.py:141 -msgid "AES128-CBC" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" msgstr "" -#: ../../common/config.py:142 -msgid "3DES-CBC" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" msgstr "" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" msgstr "" -#: ../../common/config.py:144 -msgid "Cast128-CBC" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" msgstr "" -#: ../../common/config.py:145 -msgid "AES192-CBC" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" msgstr "" -#: ../../common/config.py:146 -msgid "AES256-CBC" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" msgstr "" -#: ../../common/config.py:147 -msgid "ARCFOUR" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." msgstr "" -#: ../../common/config.py:153 -msgid "Main profile" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." msgstr "" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "" +msgstr[1] "" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." msgstr "" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" msgstr "" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" +#: common/snapshots.py:1163 +msgid "Can't create directory." msgstr "" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" +#: common/snapshots.py:1180 +msgid "Saving config file…" msgstr "" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" +#: common/snapshots.py:1261 +msgid "Saving permissions…" msgstr "" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." msgstr "" -#: ../../common/config.py:390 ../../common/config.py:424 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" msgstr "" -#: ../../common/config.py:404 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +#: common/snapshots.py:1412 +msgid "Can't remove directory" msgstr "" -#: ../../common/config.py:409 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +#: common/snapshots.py:1466 +msgid "Creating backup" msgstr "" -#: ../../common/config.py:412 ../../qt/settingsdialog.py:819 -msgid "Copy links (dereference symbolic links)" +#: common/snapshots.py:1517 +msgid "Success" msgstr "" -#: ../../common/config.py:413 ../../qt/settingsdialog.py:671 -msgid "Expert Options" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" msgstr "" -#: ../../common/config.py:415 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" -#: ../../common/config.py:1442 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" msgstr "" -#: ../../common/config.py:1446 -msgid "Failed to write new crontab." +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" msgstr "" -#: ../../common/config.py:1543 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" - -#: ../../common/config.py:1555 -#, python-format -msgid "Schedule udev doesn't work with mode %s" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -#: ../../common/config.py:1564 -#, python-format -msgid "Couldn't find UUID for \"%s\"" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" msgstr "" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." msgstr "" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" msgstr "" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" +#: common/snapshots.py:2031 +msgid "Applying retention policy" msgstr "" -#: ../../common/encfstools.py:137 -msgid "Cancel" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" msgstr "" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" msgstr "" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" msgstr "" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" msgstr "" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:797 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." msgstr "" -#: ../../common/mount.py:415 -#, python-format +#: common/sshtools.py:489 msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." msgstr "" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" +#: common/sshtools.py:726 +msgid "Remote path is not writable." msgstr "" -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: common/sshtools.py:731 +msgid "Remote path is not executable." msgstr "" -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." +#: common/sshtools.py:736 +msgid "Couldn't create remote path." msgstr "" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" msgstr "" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" msgstr "" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." msgstr "" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" +#: common/tools.py:382 +#, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." msgstr "" -#: ../../common/snapshots.py:671 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +#: common/tools.py:428 +msgid "Creation of following directory failed:" msgstr "" -#: ../../common/snapshots.py:673 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "" -msgstr[1] "" - -#: ../../common/snapshots.py:705 -#, python-format -msgid "Failed to take snapshot %s !!!" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." msgstr "" -#: ../../common/snapshots.py:714 -msgid "Finalizing" +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" -#: ../../common/snapshots.py:825 -#, python-format -msgid "Can't create folder: %s" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" -#: ../../common/snapshots.py:839 -msgid "Saving config file..." +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" msgstr "" -#: ../../common/snapshots.py:885 -msgid "Saving permissions..." +#: common/tools.py:487 +msgid "Expert Options" msgstr "" -#: ../../common/snapshots.py:967 -msgid "..." +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" -#: ../../common/snapshots.py:975 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: common/tools.py:525 +msgid "File creation failed in this directory:" msgstr "" -#: ../../common/snapshots.py:988 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" msgstr "" -#: ../../common/snapshots.py:993 -#, python-format -msgid "Can't remove folder: %s" +#: qt/aboutdlg.py:88 +msgid "Copyright:" msgstr "" -#: ../../common/snapshots.py:1027 -msgid "Taking snapshot" +#: qt/aboutdlg.py:92 +msgid "Authors:" msgstr "" -#: ../../common/snapshots.py:1061 -msgid "Nothing changed, no new snapshot necessary" +#: qt/aboutdlg.py:96 +msgid "Translators:" msgstr "" -#: ../../common/snapshots.py:1087 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" -#: ../../common/snapshots.py:1378 ../../common/snapshots.py:1429 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." msgstr "" -#: ../../common/snapshots.py:1405 -msgid "Removing old snapshots" +#: qt/aboutdlg.py:115 +msgid "this link" msgstr "" -#: ../../common/snapshots.py:1439 -msgid "Trying to keep min free space" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -#: ../../common/snapshots.py:1476 -#, python-format -msgid "Trying to keep min %d%% free inodes" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" msgstr "" -#: ../../common/snapshots.py:2052 -msgid "WITH ERRORS !" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" msgstr "" -#: ../../common/snapshots.py:2517 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" msgstr "" -#: ../../common/sshtools.py:317 +#: qt/app.py:332 +#, python-brace-format msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -#: ../../common/sshtools.py:346 -#, python-format +#: qt/app.py:337 msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"Import an existing configuration from a backup location or another computer?" msgstr "" -#: ../../common/sshtools.py:378 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +#: qt/app.py:379 +msgid "Then press OK." msgstr "" -#: ../../common/sshtools.py:429 -#, python-format -msgid "%s not found in ssh_known_hosts." +#: qt/app.py:483 +msgid "Create a backup" msgstr "" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" msgstr "" -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" +#: qt/app.py:490 +msgid "Use checksums for file change detection." msgstr "" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" msgstr "" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" msgstr "" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:682 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" msgstr "" -#: ../../common/sshtools.py:685 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:504 +msgid "Refresh backup list" msgstr "" -#: ../../common/sshtools.py:698 -#, python-format -msgid "Remote host %s doesn't support hardlinks" +#: qt/app.py:508 +msgid "Name backup" msgstr "" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/app.py:512 +msgid "Remove backup" msgstr "" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" +#: qt/app.py:516 +msgid "Open backup log" msgstr "" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" +#: qt/app.py:518 +msgid "View log of the selected backup." msgstr "" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" +#: qt/app.py:520 +msgid "Open last backup log" msgstr "" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" +#: qt/app.py:522 +msgid "View log of the latest backup." msgstr "" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/app.py:524 +msgid "Manage profiles…" msgstr "" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" +#: qt/app.py:528 +msgid "Edit user-callback" msgstr "" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" +#: qt/app.py:532 +msgid "Shutdown" msgstr "" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." msgstr "" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" +#: qt/app.py:536 +msgid "Setup language…" msgstr "" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" +#: qt/app.py:540 +msgid "Exit" msgstr "" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" +#: qt/app.py:550 +msgid "man page: Back In Time" msgstr "" -#: ../../qt/app.py:142 -msgid "Shutdown" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" msgstr "" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/app.py:555 +msgid "man page: Profiles config file" msgstr "" -#: ../../qt/app.py:149 -msgid "Exit" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" msgstr "" -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" msgstr "" -#: ../../qt/app.py:163 -msgid "Website" +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" +#: qt/app.py:573 +msgid "FAQ" msgstr "" -#: ../../qt/app.py:167 -msgid "FAQ" +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" msgstr "" -#: ../../qt/app.py:169 +#: qt/app.py:577 msgid "Ask a question" msgstr "" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" +#: qt/app.py:584 +msgid "Translation" msgstr "" -#: ../../qt/app.py:204 -msgid "Up" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." msgstr "" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1842 -msgid "Show hidden files" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" msgstr "" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1884 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." msgstr "" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:599 +msgid "About" msgstr "" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" msgstr "" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." msgstr "" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" msgstr "" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." msgstr "" -#: ../../qt/app.py:249 -#, python-format +#: qt/app.py:615 msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:201 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" +#: qt/app.py:621 +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" -#: ../../qt/app.py:260 -msgid "Snapshot" +#: qt/app.py:624 +msgid "Up" msgstr "" -#: ../../qt/app.py:271 -msgid "View" +#: qt/app.py:627 +msgid "Show hidden files" msgstr "" -#: ../../qt/app.py:321 -msgid "Shortcuts" +#: qt/app.py:630 +msgid "Compare backups…" msgstr "" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" msgstr "" -#: ../../qt/app.py:398 -msgid "Add to Include" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" +#: qt/app.py:700 +msgid "Back In &Time" msgstr "" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:705 +msgid "&Backup" msgstr "" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:717 +msgid "&Restore" msgstr "" -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +#: qt/app.py:723 +msgid "&Help" msgstr "" -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../qt/app.py:723 -msgid "Error:" +#: qt/app.py:784 +msgid "Light icon" msgstr "" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:189 -msgid "Sent:" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:190 -msgid "Speed:" +#: qt/app.py:808 +msgid "Icons only" msgstr "" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:191 -msgid "ETA:" +#: qt/app.py:811 +msgid "Text only" msgstr "" -#: ../../qt/app.py:794 -msgid "Global" +#: qt/app.py:814 +msgid "Text below icons" msgstr "" -#: ../../qt/app.py:795 -msgid "Root" +#: qt/app.py:817 +msgid "Text beside icon" msgstr "" -#: ../../qt/app.py:796 -msgid "Home" +#: qt/app.py:943 +msgid "This directory doesn't exist in the selected backup." msgstr "" -#: ../../qt/app.py:811 -msgid "Backup folders" +#: qt/app.py:946 +msgid "This directory doesn't exist on the computer." msgstr "" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:995 msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +#: qt/app.py:998 +msgid "Close the window anyway?" msgstr "" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/app.py:1189 +msgid "Done, no backup needed" msgstr "" -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +#: qt/app.py:1260 +msgid "Working:" msgstr "" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" +#: qt/app.py:1267 +msgid "Working" msgstr "" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" msgstr "" -#: ../../qt/app.py:1083 -#, python-format -msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" msgstr "" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" msgstr "" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" msgstr "" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" +#: qt/app.py:1498 +msgid "Backup:" msgstr "" -#: ../../qt/app.py:1105 -msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" msgstr "" -#: ../../qt/app.py:1307 -msgid "View the current disk content" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" msgstr "" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" msgstr "" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" +#: qt/app.py:1653 +msgid "translation platform" msgstr "" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" +#: qt/app.py:1658 +msgid "Website" msgstr "" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." +#: qt/app.py:1672 +msgid "Your translation" msgstr "" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." msgstr "" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." msgstr "" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." msgstr "" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." msgstr "" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" +#: qt/app.py:1725 +msgid "Open an issue" msgstr "" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1971 -msgid "Profile:" +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." msgstr "" -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" +#: qt/app.py:1841 +msgid "Proceed with the backup?" msgstr "" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" +#: qt/app.py:1875 +#, python-brace-format +msgid "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" +#: qt/app.py:2107 +msgid "Backup name" msgstr "" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" +msgstr[1] "" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" msgstr "" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" -#: ../../qt/messagebox.py:58 -msgid "Error" +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." msgstr "" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." msgstr "" -#: ../../qt/qttools.py:221 -msgid "Today" -msgstr "" +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +msgstr[1] "" -#: ../../qt/qttools.py:225 -msgid "Yesterday" -msgstr "" +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "" +msgstr[1] "" -#: ../../qt/qttools.py:230 -msgid "This week" +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" msgstr "" -#: ../../qt/qttools.py:234 -msgid "Last week" +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" -#: ../../qt/qttools.py:353 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" msgstr "" -#: ../../qt/qttools.py:355 -#, python-format -msgid "Last check %s" +#: qt/fileview.py:204 +msgid "Add to Include" msgstr "" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/fileview.py:205 +msgid "Add to Exclude" msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "" +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" msgstr "" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1973 -msgid "Mode:" +#: qt/languagedialog.py:132 +msgid "System default" msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/languagedialog.py:142 +msgid "Use operating system's language." msgstr "" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1613 -msgid "Where to save snapshots" +#: qt/logviewdialog.py:63 +msgid "Backup Log View" msgstr "" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" +#: qt/logviewdialog.py:63 +msgid "Last Log View" msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" msgstr "" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" +#: qt/logviewdialog.py:86 +msgid "Backups:" msgstr "" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" +#: qt/logviewdialog.py:93 +msgid "Filter:" msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "" +msgstr[1] "" + +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" msgstr "" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" msgstr "" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1693 -msgid "Full snapshot path: " +#: qt/manageprofiles/__init__.py:112 +msgid "&General" msgstr "" -#: ../../qt/settingsdialog.py:307 -msgid "Schedule" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" msgstr "" -#: ../../qt/settingsdialog.py:317 -msgid "Day:" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" msgstr "" -#: ../../qt/settingsdialog.py:328 -msgid "Weekday:" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" msgstr "" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" msgstr "" -#: ../../qt/settingsdialog.py:350 -msgid "Hours:" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" msgstr "" -#: ../../qt/settingsdialog.py:359 -msgid "" -"Run Back In Time repeatedly. This is useful if the computer is not running " -"regularly." +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" msgstr "" -#: ../../qt/settingsdialog.py:364 -msgid "Every:" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" msgstr "" -#: ../../qt/settingsdialog.py:382 -msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" msgstr "" -#: ../../qt/settingsdialog.py:395 -msgid "Include" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" msgstr "" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." msgstr "" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." msgstr "" -#: ../../qt/settingsdialog.py:434 +#: qt/manageprofiles/copylinkswidget.py:60 msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" msgstr "" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" msgstr "" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" msgstr "" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" msgstr "" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" msgstr "" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" msgstr "" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" msgstr "" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" msgstr "" -#: ../../qt/settingsdialog.py:610 -msgid "Options" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" msgstr "" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" msgstr "" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" msgstr "" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" msgstr "" -#: ../../qt/settingsdialog.py:625 -msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/settingsdialog.py:658 -msgid "None" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'rsync' with 'nice':" +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" msgstr "" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" msgstr "" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:720 -msgid "on remote host" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'rsync' with 'ionice':" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" msgstr "" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" msgstr "" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" msgstr "" -#: ../../qt/settingsdialog.py:710 -msgid "(Please install 'nocache' to enable this option)" +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -#: ../../qt/settingsdialog.py:716 -msgid "on local machine" +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" msgstr "" -#: ../../qt/settingsdialog.py:723 -msgid "Redirect stdout to /dev/null in cronjobs." +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/settingsdialog.py:729 -msgid "Redirect stderr to /dev/null in cronjobs." +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" msgstr "" -#: ../../qt/settingsdialog.py:738 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" msgstr "" -#: ../../qt/settingsdialog.py:741 -msgid " KB/sec" +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" msgstr "" -#: ../../qt/settingsdialog.py:776 -msgid "Preserve ACL" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" msgstr "" -#: ../../qt/settingsdialog.py:789 -msgid "Preserve extended attributes (xattr)" +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" msgstr "" -#: ../../qt/settingsdialog.py:807 -msgid "Copy unsafe links (works only with absolute links)" +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" msgstr "" -#: ../../qt/settingsdialog.py:838 -msgid "Paste additional options to rsync" +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" msgstr "" -#: ../../qt/settingsdialog.py:841 +#: qt/manageprofiles/schedulewidget.py:93 msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" -#: ../../qt/settingsdialog.py:851 -msgid "Add prefix to SSH commands" +#: qt/manageprofiles/schedulewidget.py:98 +msgid "" +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." msgstr "" -#: ../../qt/settingsdialog.py:854 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" msgstr "" -#: ../../qt/settingsdialog.py:862 ../../qt/settingsdialog.py:1744 -msgid "default" +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" msgstr "" -#: ../../qt/settingsdialog.py:872 -msgid "Check if remote host is online" +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" -#: ../../qt/settingsdialog.py:873 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -#: ../../qt/settingsdialog.py:876 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "" +msgstr[1] "" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "" +msgstr[1] "" + +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" msgstr "" -#: ../../qt/settingsdialog.py:877 +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" -#: ../../qt/settingsdialog.py:890 -msgid "Restore Config" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/sshkeyselector.py:65 +#, fuzzy +msgid "Choose an existing private key file from somewhere else." msgstr "" +"Velg en eksisterende privat nøkkelfil (normalt kalt «id_ed25519», og i eldre" +" oppsett «id_rsa»)." -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +#, fuzzy +msgid "Create a new SSH key without passphrase." +msgstr "Mislyktes å lage ny SSH-nøkkel i {path}." -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, fuzzy, python-brace-format +msgid "Full path: {path}" +msgstr "Full sti til øyeblikksbilde:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "Privat SSH-nøkkel:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +#, fuzzy +msgid "Use system SSH configuration" +msgstr "Importer konfigurasjon" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Ikke fjern navngitte øyeblikksbilder" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH-proxy" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Alternativer" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Tjener:" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Slå på meldinger" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Slå av funksjonen snapshot ved bateridrift" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Bruker:" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Koble til målverten via denne proxyen (også kjent som en hoppvert). Se «-J» " +"i dokumentasjonen for SSH-kommandoen eller «ProxyJump» i man-siden for " +"«ssh_config» for detaljer." + +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Info{ENDBOLD}: I «SSH kryptert» modus fungerer kun enkle eller doble " +"asterisk (f.eks. {example2}). Andre typer jokertegn og mønstre vil bli " +"ignorert (f.eks. {example1}). Filnavn er uforutsigbare i denne modusen på " +"grunn av kryptering med EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Ekskluder mønstre, filer eller mapper" + +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Ekskluder mønster" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Legg til fil" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Legg til mappe" + +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "Spørsmål" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Ekskluder filer som er større enn:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Ekskluder filer som er større enn {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 +#, fuzzy msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Med «Full rsync mode» avslått vil dette kun påvirke nye filer ettersom for " +"rsync er dette et overføringsvalg, ikke et ekskluderingsvalg. Derfor vil " +"store filer som tidligere har blitt sikkerhetskopiert bli værende i " +"øyeblikksbildet selv om de har blitt endret." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Ekskluder mønster" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Ekskluder mønster" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" msgstr "" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Ekskluder fil" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Ekskluder mappe" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" +"Deaktivert fordi dette mønsteret ikke fungerer i «SSH kryptert» modus." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Disse innstillingene er for avanserte konfigurasjoner. Endre dem kun dersom " +"du er fullt klar over konsekvensene." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Kjør «rsync» med «{cmd}»:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "som cron-jobb" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" - -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +msgstr "på ekstern vert" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +#, fuzzy +msgid "when taking a manual backup" +msgstr "når du tar et manuelt øyeblikksbilde" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Vennligst installer «nocache» for å slå på dette valget." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "på lokal maskin" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Videresend stdout til /dev/null i cron-jobber." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron vil automatisk sende en e-post med vedlagt utdata fra cron-jobber " +"dersom en MTA er installert." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Videresend stderr til /dev/null i cron-jobber." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron vil automatisk sende en e-post med vedlagte feilmeldinger fra cron-" +"jobber dersom en MTA er installert." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/sek" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Begrens båndbredden for rsync til:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "" +msgstr "Behold ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "" +msgstr "Behold utvidede attributter (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Begrens til ett filsystem" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Valgene må settes i anførselstegn, for eksempel {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Lim inn tilleggsopsjoner til rsync" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Et prefiks som skal kjøres før hver kommando på ekstern vert." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Variabler må innledes med en bakoverstrek, for eksempel \\$FOO. Dette " +"påvirker ikke rsync. For å legge til et prefiks for rsync, bruk " +"«{example_value}» sammen med {rsync_options_value}." + +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "standard" -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" +msgstr "Legg til et prefiks for SSH-kommandoer" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Kontroller at den eksterne verten er tilgjengelig" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Advarsel: Om dette valget er avslått, og den eksterne verten ikke er " +"tilgjengelig, kan det oppstå en del merkelige feil." + +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Kontroller at den eksterne verten støtter alle nødvendige kommandoer." -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Advarsel: Om dette valget er avslått, og den eksterne verten ikke støtter " +"alle nødvendige kommandoer, kan det oppstå en del merkelige feil." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(standard: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "avslått" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "påslått" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Modus:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Hvor skal øyeblikksbilder lagres" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH-innstillinger" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Sti:" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "Ny profil" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Passord" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Lagre passord til nøkkelringen" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Legg passordet i hurtigminne for cron (sikkerhetsrisiko: root kan lese " +"passordet)" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avansert" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +#, fuzzy +msgid "Full backup path:" +msgstr "Full sti til øyeblikksbilde:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." msgstr "" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Passordene stemmer ikke overens." + +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:560 +#, fuzzy +msgid "Proceed with copying the SSH key?" +msgstr "Fortsett med sikkerhetskopieringen?" + +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Ektheten til vert {host} kan ikke fastslås." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "Fingeravtrykket til {keytype}-nøkkelen er:" + +#: qt/manageprofiles/tab_general.py:613 +#, fuzzy +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Vennligst kontroller dette fingeravtrykket. Vil du legge det til i filen " +"«known_hosts»?" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Opprett en ny kryptert mappe?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "" + +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "privat SSH-nøkkel" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Omdøp profil" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Mislyktes å lage ny SSH-nøkkel i {path}." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Er du sikker på at du vil slette profilen «% s\"?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Inkluder filer og mapper" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"«{path}» er en symbolsk lenke. Målet for lenken sikkerhetskopieres ikke før " +"du inkluderer det også." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Inkluder heller målet til den symbolske lenken i stedet?" + +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Inkluder fil" + +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Inkluder mappe" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Slå på varslinger" + +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" +msgstr "Slå av øyeblikksbilder ved batteridrift" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Strømstatus er ikke tilgjengelig fra systemet" + +#: qt/manageprofiles/tab_options.py:52 +#, fuzzy +msgid "Run only one backup at a time" +msgstr "Ta kun ett øyeblikksbilde om gangen" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:56 +#, fuzzy msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Andre øyeblikksbilder vil bli blokkert inntil det nåværende øyeblikksbildet " +"er fullført. Dette er et globalt valg, så det vil påvirke alle profiler for " +"denne brukeren. Du må imidlertid slå dette på for alle andre brukere." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Sikkerhetskopier filer som overskrives ved gjenoppretting" + +#: qt/manageprofiles/tab_options.py:78 +#, fuzzy +msgid "Continue on errors (keep incomplete backups)" +msgstr "Fortsett ved feil (behold ufullstendige øyeblikksbilder)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Bruk sjekksum for å oppdage endringer" + +#: qt/manageprofiles/tab_options.py:86 +#, fuzzy +msgid "Create a new backup whether there were changes or not." msgstr "" +"Ta et nytt øyeblikksbilde, uavhengig av om det har vært endringer eller " +"ikke." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Advar hvis ledig diskplass faller under" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Viser en advarsel når ledig plass på plasseringen for sikkerhetskopi er " +"mindre enn den angitte verdien." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Dersom regelen Fjern & Oppbevaring er aktivert, og eldre sikkerhetskopier " +"fjernes basert på tilgjengelig ledig plass, kan ikke denne verdien være " +"lavere enn verdien i regelen." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Logg-nivå:" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Ingen" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "brukerhåndbok" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, fuzzy, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Følgende regler behandles fra topp til bunn. Senere regler overstyrer " +"tidligere, og de er ikke begrenset av disse. Se {manual} for detaljer og " +"eksempler." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Utelukk mønster" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Åpne brukerhåndboken i nettleseren." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Ekskluder fil" +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Behold det nyeste øyeblikksbildet." + +#: qt/manageprofiles/tab_remove_retention.py:241 +#, fuzzy +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" +"Det siste eller nyeste øyeblikksbildet beholdes under alle omstendigheter." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Denne oppførselen kan ikke endres." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Ekskludér katalog" +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Behold navngitte øyeblikksbilder." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" +#: qt/manageprofiles/tab_remove_retention.py:258 +#, fuzzy +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Øyeblikksbilder som har blitt gitt et navn, i tillegg til det vanlige " +"tidsstempelet, beholdes under alle omstendigheter og fjernes ikke." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "År" + +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Fjern øyeblikksbilder eldre enn" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Hele dager. Dagens dato ignoreres." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "Kalenderuker med mandag som første dag. Nåværende uke ignoreres." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12-måneders perioder. Nåværende måned ignoreres." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Oppbevaringsregel" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Kjør i bakgrunnen på den eksterne verten." + +#: qt/manageprofiles/tab_remove_retention.py:313 +#, fuzzy msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Den smarte fjerningsprosedyren kjøres direkte på den eksterne maskinen, ikke" +" lokalt. Kommandoene «bash», «screen» og «flock» må være installert og " +"tilgjengelige på den eksterne maskinen." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Hvis valgt, vil Back In Time først teste den eksterne maskinen." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dager telles med utgangspunkt fra i dag." + +#: qt/manageprofiles/tab_remove_retention.py:322 +#, fuzzy +msgid "Keep all backups for the last" +msgstr "Behold alle øyeblikksbilder tatt de siste" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "dag(ene)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +#, fuzzy +msgid "Keep the last backup for each day for the last" +msgstr "Behold det siste øyeblikksbildet for hver dag tatt de siste" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Ukene telles med utgangspunkt i den nåværende uken. En uke starter på " +"mandag." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Inkludér katalog" +#: qt/manageprofiles/tab_remove_retention.py:347 +#, fuzzy +msgid "Keep the last backup for each week for the last" +msgstr "Behold det siste øyeblikksbildet for hver uke tatt de siste" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Er du sikker på at vil bytte øyeblikksbilder mappen ?" +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "uke(ene)." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." msgstr "" +"Månedene regnes som kalendermåneder, med utgangspunkt i den nåværende " +"måneden." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:360 +#, fuzzy +msgid "Keep the last backup for each month for the last" +msgstr "Behold det siste øyeblikksbildet for hver måned tatt de siste" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "måned(ene)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Årene telles som kalenderår med utgangspunkt i det nåværende året." + +#: qt/manageprofiles/tab_remove_retention.py:372 +#, fuzzy +msgid "Keep the last backup for each year for" +msgstr "Behold det siste øyeblikksbildet for hvert år for" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "alle år." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… den ledige plassen er mindre enn" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… ledige inoder er mindre enn" + +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Slett de eldste øyeblikksbildene om …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Snarveier" + +#: qt/placeswidget.py:96 +msgid "Places" msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/placeswidget.py:97 +msgid "File System" msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Mapper for sikkerhetskopiering" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: {profile_name}" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Vis siste logg" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Start {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Arbeider…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importer konfigurasjon" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importer" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Arbeider…" + +#: qt/restoreconfigdialog.py:211 +#, fuzzy, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Velg mappen med øyeblikksbilder hvorfra konfigurasjonsfilen skal importeres." +" Stien kan se slik ut: {samplePath}" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Om mappen befinner seg på et flyttbar eller eksternt medium, må den monteres" +" manuelt først." -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Diff Valg" +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Vis skjulte filer" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Inkluder filer og mapper" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Filoppretting mislyktes i denne mappen:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Vis hele loggen" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Nedtelling for avslutning" + +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Skrur av systemet etter at øyeblikksbildet er ferdig." + +#: qt/shutdowndlg.py:36 +#, fuzzy +msgid "Cancel Shutdown" +msgstr "Skru av" + +#: qt/shutdowndlg.py:37 +#, fuzzy +msgid "Shutdown Now" +msgstr "Skru av" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Systemet vil bli slått av om {n} sekund." +msgstr[1] "Systemet vil bli slått av om {n} sekunder." + +#: qt/snapshotsdialog.py:61 +#, fuzzy +msgid "Options about comparing backups" +msgstr "Opsjoner for å sammenligne øyeblikksbilder" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Kommando:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" -msgstr "Parametere:" +msgstr "Parametre:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "Bruk %1 and %2 for path parameters" +msgstr "Bruk %1 og %2 for sti-parametre" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Vennligst angi en diff-kommando, eller trykk avbryt." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"Kommandoen «{cmd}» ble ikke funnet på dette systemet. Prøv noe annet, eller " +"trykk avbryt." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Ingen parametrer er angitt for diff-kommandoen. Bruker standardverdien " +"«{params}»." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +#, fuzzy +msgid "Backups" +msgstr "&Sikkerhetskopi" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Kun øyeblikksbilder med forskjeller" + +#: qt/snapshotsdialog.py:159 +#, fuzzy +msgid "List only backups that are equal to:" +msgstr "Vis kun øyeblikksbilder som er identiske med:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Grundig sjekk (mer nøyaktig, men treg)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Slett" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Velg alle" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Sammenlign" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" -msgstr "Gå Til" +msgstr "Gå til" + +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Alternativer" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Det er ikke mulig å sammenligne en sikkerhetskopi med seg selv, da " +"sammenligningen vil være unødvendig." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "" +"Ønsker du virkelig å slette {file_or_dir} i sikkerhetkopien {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Ønsker du virkelig å slette {file_or_dir} i {count} sikkerhetskopier?" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Du kan ikke sammenlikne et øyeblikksbilde med seg selv" +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "ADVARSEL: Dette kan ikke angres." -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Kommando ikke funnet: %s" +#: qt/snapshotsdialog.py:495 +#, fuzzy, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Utelat {path} fra fremtidige øyeblikksbilder?" + +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Rot" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "I dag" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "I går" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Denne uken" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Forrige uke" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Sist sjekket {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Dette er IKKE en sikkerhetskopi, men en sanntidsvisning av de lokale filene." diff --git a/common/po/nl.po b/common/po/nl.po index 6943dce49..d906d4ae0 100644 --- a/common/po/nl.po +++ b/common/po/nl.po @@ -1,547 +1,384 @@ -# Dutch translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2018-05-13 20:27+0000\n" -"Last-Translator: rob \n" -"Language-Team: Dutch \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-05 17:33+0000\n" +"Last-Translator: buhtz \n" +"Language-Team: Dutch \n" +"Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Kon volgende configuratie niet opslaan: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Kon volgende configuratie niet laden: %s" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Profiel \"%s\" bestaat al!" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "U kunt het laatste profiel niet verwijderen!" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Uitgeschakeld" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Bij elke start/herstart" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Elke 5 minuten" - -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Elke 10 minuten" - -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Elke 30 minuten" - -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Elk uur" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Elke 2 uren" - -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Elke 4 uren" - -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Elke 6 uren" - -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Elke 12 uren" - -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Aangepaste uren" - -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Elke dag" - -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Herhaaldelijk (anacron)" - -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Wanneer er een extern medium aangesloten wordt (udev)" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Elke week" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Elke maand" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Dag(en)" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Week/weken" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" +"Alle licenties in dit project bevinden zich in de {dir_link} map. Om de per-" +"bestand licenties en auteursrecht informatie te extraheren, zie " +"{readme_link}." -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Jaar/jaren" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Opgelet" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Uur/uren" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Hoofdprofiel" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Maand(en)" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Lokaal (EncFS-versleuteld)" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " EXPERIMENTEEL!" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS-versleuteld)" -#: ../../common/config.py:129 +#: common/config.py:237 msgid "Local" msgstr "Lokaal" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" - -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "SSH-privésleutel" - -#: ../../common/config.py:131 +#: common/config.py:240 msgid "Local encrypted" msgstr "Lokaal versleuteld" -#: ../../common/config.py:131 ../../common/config.py:132 +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 msgid "Encryption" msgstr "Versleuteling" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH-versleuteld" - -#: ../../common/config.py:135 -msgid "Default" -msgstr "Standaardwaarde" - -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" - -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" - -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" - -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" - -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" - -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" - -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" - -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" - -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" - -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" - -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" - -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" - -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Hoofdprofiel" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profiel: \"%s\"" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Reservekopiemap is niet geldig!" - -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Selecteer tenminste één map om een reservekopie te kunnen maken !" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "U kunt de reservekopiemap niet toevoegen!" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH private sleutel" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "U kunt geen submap van de reservekopiemap toevoegen!" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profiel: “{name}”" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s is geen map!" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Backup-folder is ongeldig." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Gastcomputer/Gebruiker/Profiel-ID mag niet leeg zijn!" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Er moet minstens één backup map geselecteerd worden." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Kan niet schrijven naar: %s\n" -"Heeft u schrijfrechten?" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Map: {path}" -#: ../../common/config.py:402 -#, python-format +#: common/config.py:332 common/config.py:347 msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" -"Doelbestandssysteem voor '%(path)s' is geformatteerd als FAT dat geen harde " -"links ondersteunt. Gebruik hiervoor in de plaats a.u.b een Linux-" -"bestandssysteem." +"Deze map kan niet opgenomen worden in de back-up, want ze is onderdeel is " +"van de back-up bestemming zelf." -#: ../../common/config.py:407 -#, python-format +#: common/config.py:366 +#, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" -"Doelbestandssysteem voor '%(path)s' is een aangekoppeld SMB-netwerk. Zorg er " -"a.u.b. voor dat de SMB-server op afstand symbolische links ondersteunt of " -"activeer '%(copyLinks)s' in '%(expertOptions)s'." - -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Links kopiëren (symbolische links dereferentie)" +"De waarde voor \"Verwijder oudste backup indien de vrije plaats kleiner is " +"dan\" ({val_one}) moet kleiner dan of gelijk zijn aan de grenswaarde voor " +"\"Waarschuw als de vrije schijfruimte minder is dan\" ({val_two})." -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Geavanceerde opties" - -#: ../../common/config.py:413 -#, python-format +#: common/config.py:371 msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" -"Doelbestandssysteem voor '%(path)s' is een aangekoppeld sshfs-netwerk dat " -"geen harde links ondersteunt. Gebruik hiervoor in de plaats a.u.b. de 'SSH-" -"modus'." +"Pas aub. de waarden aan zodat limiet om backups te verwijderen niet groter " +"is dan de limiet voor de waarschuwing." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Kan crontab niet vinden.\n" -"Weet u zeker dat cron geïnstalleerd is?\n" -"Indien dit niet het geval is, dient u het maken van automatische " -"reservekopieën uit te schakelen." +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Udev methode werkt niet bij modus {mode}" -#: ../../common/config.py:1478 +#: common/config.py:1569 msgid "Failed to write new crontab." msgstr "Kon geen nieuwe crontab schrijven." -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"Kon udev-regel voor profiel %(profile_id)s niet installeren. DBus-dienst " -"'%(dbus_interface)s' was niet beschikbaar" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Planning udev werkt niet bij modus %s" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Kon de unieke identificatiecode voor \"%s\" niet vinden" - -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" -"Kan '%(command)s' niet aankoppelen :\n" -"\n" -"%(error)s" - -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "Configuratie voor versleutelde map niet gevonden." - -#: ../../common/encfstools.py:136 +#: common/config.py:1577 msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Nieuwe versleutelde map aanmaken?" - -#: ../../common/encfstools.py:137 +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron is niet actief, hoewel het commando crontab beschikbaar is. Geplande " +"back-up taken worden niet uitgevoerd. Mogelijk is cron geïnstalleerd, maar " +"niet geactiveerd. Probeer het commando \"systemctl enable cron\" of " +"raadpleeg de ondersteuningskanalen van uw GNU/Linux-distributie." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Kon configuratie niet opslaan" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Kon configuratie niet laden" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Profiel \"{name}\" bestaat al." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Het laatste profiel kan niet worden verwijderd." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Kan \"{command}\" niet aankoppelen" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Configuratie voor de versleutelde map niet gevonden." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Een nieuwe versleutelde map aanmaken?" + +#: common/encfstools.py:204 msgid "Cancel" msgstr "Annuleren" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Wachtwoord bevestigen" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Voer het EncFS wachtwoord opnieuw in om te bevestigen." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Wachtwoord komt niet overeen" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "De EncFS wachtwoorden komen niet overeen." -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" -"encfs-versie 1.7.2 en daarvoor hebben een fout met de optie --reverse. Werk " -"encfs a.u.b. bij" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 +#: common/encfstools.py:659 common/snapshots.py:1128 msgid "Take snapshot" -msgstr "Reservekopie maken" - -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" -"Er heeft een Hash collision plaatsgevonden in hash_id %s. Vergroot de " -"globale waarde voor hash_collision en probeer het opnieuw." +msgstr "Momentopname maken" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "Kon %(proc)s niet ontkoppelen van %(mountpoint)s" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Kan het geëncrypteerde pad \"{command}\" niet initialiseren" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "%(proc)s niet gevonden. Installeer b.v. %(install_command)s" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Kan {mountprocess} niet ontkoppelen van {mountpoint}." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" -"%(user)s is geen lid van de groep 'fuse'.\n" -" Voer de opdracht 'sudo adduser %(user)s fuse' uit. Om de wijzigingen toe te " -"passen dient u zich eerst af te melden en daarna weer aan te melden.\n" -"Bekijk de 'Back In Time-handleiding' voor verdere instructies." - -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "koppelpunt-%s niet leeg." +"{command} niet gevonden. Installeer het a.u.b. (bijv. via " +"\"{installcommand}\")" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "Time-out aankoppelprocesvergrendeling" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Koppelpunt {mntpoint} niet leeg." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Profiel '%(profile)s': Voer wachtwoord in voor %(mode)s: " +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Voer het wachtwoord in voor {mode} profiel \"{profile}\":" -#: ../../common/snapshotlog.py:62 +#: common/schedule.py:238 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" -"### Dit logboek is gedecodeerd met automatisch zoekpatroon\n" -"### Als sommige paden niet gedecodeerd zijn dan kunt u deze handmatig " -"decoderen met:\n" +"Kon Udev-regel voor profiel {profile_id} niet installeren. DBus-dienst " +"'{dbus_interface}' was niet beschikbaar." -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "" +"Kon UUID (Universeel Unieke IDentificatiecode) voor \"{path}\" niet vinden" + +#: common/snapshots.py:378 common/snapshots.py:656 msgid "FAILED" msgstr "MISLUKT" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Rechten herstellen:" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Rechten herstellen" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 msgid "Done" msgstr "Voltooid" -#: ../../common/snapshots.py:609 +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"De volgende items uit de op te nemen lijst komen niet overeen met een " +"bestand of map in de backup bron:" + +#: common/snapshots.py:812 msgid "Deferring backup while on battery" -msgstr "Maken van reservekopieën uitstellen indien op accu" +msgstr "Back-up uitstellen indien op batterij" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Kan de back-up map niet vinden." + +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." msgstr "" -"Kan reservekopiemap niet vinden.\n" -"Als deze op een extern medium staat, gelieve dit aan te sluiten." +"Als deze op een verwijderbaar medium staat, gelieve dit aan te sluiten." + +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Wacht {n} seconde." +msgstr[1] "Wacht {n} seconden." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Wacht %s seconde" -msgstr[1] "Wacht %s seconden" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Kon back-up {snapshot_id} niet maken." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Kon reservekopie %s niet maken!!!" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Even geduld. Afronden…" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Voltooien" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Kan map niet aanmaken." -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Kan deze map niet aanmaken: %s" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Configuratiebestand opslaan…" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Configuratiebestand opslaan..." +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Rechten opslaan…" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Rechten opslaan..." +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "Onvolledige back-up {snapshot_id} gevonden die kan voortgezet worden." -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Onvolledige map {snapshot_id} van de laatste keer wordt verwijderd" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "Restant '%s' gevonden waarmee u verder kunt gaan." +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Kan map niet verwijderen" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "De van de laatste keer overgebleven map '%s' verwijderen" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Aanmaken backup" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Kan deze map niet verwijderen: %s" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Succes" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Reservekopie maken" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Gedeeltelijke overdracht vanwege een fout" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" -"Er is niets gewijzigd, dus hoeft er geen nieuwe reservekopie gemaakt te " -"worden" +"Gedeeltelijke overdracht vanwege verdwenen bronbestanden (zie 'man rsync')" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Kan %(new_path)s niet hernoemen naar %(path)s" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' eindigde met exitcode {exit_code}" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Slim verwijderen" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Zie 'man rsync' voor meer informatie" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Oude reservekopieën verwijderen" +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "" +"Negatieve rsync-exitcodes zijn signaalnummers, zie 'kill -l' en 'man kill'" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Probeer een minimum aan vrije ruimte te houden" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Er is niets veranderd, geen nieuwe back-up nodig" + +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Kan {new_path} niet hernoemen naar {path}." -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Probeer een minimum van %d%% vrije inodes te houden" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Regels om oude back-ups te verwijderen worden toegepast" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "MET FOUTEN!" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Bewaarbeleid wordt toegepast" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Een minimum aan vrije ruimte proberen te behouden" + +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Een minimum van {perc} vrije inodes proberen te behouden" + +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 msgid "Now" -msgstr "Huidige schijfinhoud" +msgstr "Nu" + +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Kan {sshfs} niet aankoppelen" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Kan %s niet aankoppelen" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "" +"ssh-agent niet gevonden. Gelieve te controleren of deze is geïnstalleerd." -#: ../../common/sshtools.py:315 +#: common/sshtools.py:489 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." @@ -549,1285 +386,2230 @@ msgstr "" "Kon SSH-privésleutel niet ontgrendelen. Het wachtwoord is ongeldig of is " "niet beschikbaar voor cron." -#: ../../common/sshtools.py:345 -#, python-format +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Extern pad bestaat maar is geen map." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Extern pad is niet beschrijfbaar." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Extern pad is niet uitvoerbaar." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Kon extern pad niet aanmaken." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Externe host {host} ondersteunt {command} niet" + +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Controlecommando's op host {host} gaven onbekende foutmelding" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Externe host {host} ondersteunt geen harde koppelingen" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "De openbare SSH-sleutel \"{pubkey}\" kopiëren naar externe host \"{host}\"." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Voer het wachtwoord in voor \"{user}\"." + +#: common/tools.py:382 +#, python-brace-format msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"Authenticatie aanmelden zonder wachtwoord voor %(user)s@%(host)s is mislukt. " -"Bekijk de 'Back In Time-handleiding' voor verdere instructies." +"Het doelbestandssysteem van ‘{path}’ is geformatteerd als NTFS, dat geen " +"harde koppeling, hard-links, ondersteunt. Gebruik in plaats daarvan een " +"authentiek Linux-bestandssysteem." -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" -"Codering %(cipher)s mislukt voor %(host)s:\n" -"%(err)s" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} is geen geldige map." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Aanmaken van de volgende map is mislukt:" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s is niet gevonden in de ssh_bekende_gastcomputers." +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Schrijftoegang kan begrensd zijn." -#: ../../common/sshtools.py:468 -#, python-format +#: common/tools.py:471 +#, python-brace-format msgid "" -"Remote path exists but is not a directory:\n" -" %s" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" -"Pad op afstand bestaat maar is geen map:\n" -" %s" +"Het doel-bestandssysteem van {path} is geformatteerd als FAT, dat geen harde" +" koppelingen ondersteunt. Gebruik in plaats daarvan een ondersteund " +"GNU/Linux-bestandssysteem." -#: ../../common/sshtools.py:470 -#, python-format +#: common/tools.py:482 +#, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" -"Pad op afstand is niet beschrijfbaar:\n" -" %s" +"Het doel-bestandssysteem van {path} is een met SMB aangekoppelde " +"netwerkopslag. Zorg ervoor dat de externe SMB-server symbolische koppelingen" +" ondersteunt of activeer \"{copyLinks}\" in \"{expertOptions}\"." + +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Koppelingen kopiëren (symbolische koppelingen derefereren)" + +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Geavanceerde opties" -#: ../../common/sshtools.py:472 -#, python-format +#: common/tools.py:491 +#, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" -"Pad op afstand is niet uitvoerbaar:\n" -" %s" +"Doel-bestandssysteem voor {path} is een met sshfs aangekoppelde " +"netwerkopslag. Sshfs ondersteunt geen harde koppelingen. Gebruik in plaats " +"daarvan de modus \"SSH\"." -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Kon geen bestand aanmaken in deze map:" + +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Over Back In Time" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Copyright:" + +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Auteurs:" + +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Vertalingen:" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" -"Kon geen pad op afstand aanmaken:\n" -" %s" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Geen vertaler credits bechikbaar voor de huidige taal." + +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "deze link" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "Volg {thislink} om vertaler credits te verkrijgen voor alle talen." + +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Projectwebsite" + +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Gebruikershandleiding" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -"Pingen van %s is mislukt. Gastcomputer is uitgeschakeld of er is een " -"verkeerd adres gebruikt." +"Open de gebuikershandleiding in de browser (lokaal indien beschikbaar, " +"anders online)" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Versie{BOLDEND}: {version}" + +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -"Gastcomputer op afstand %(host)s ondersteunt geen '%(command)s':\n" -"%(err)s\n" -"Bekijk de 'Back In Time-handleiding' voor verdere instructies" +"Het lijkt erop dat {app_name} voor de eerste keer wordt uitgevoerd, want er " +"is geen configuratie gevonden." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/app.py:337 msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Import an existing configuration from a backup location or another computer?" msgstr "" -"Controleer de opdrachten op de gastcomputer. %(host)s gaf een onbekende fout " -"aan:\n" -"%(err)s\n" -"Bekijk de 'Back In Time-handleiding' voor verdere instructies" +"Een bestaande configuratie importeren vanuit een back-up doelmap of een " +"andere computer?" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "Gastcomputer op afstand %s ondersteunt geen harde links" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Druk vervolgens op OK." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Maak een backup" + +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" -"Kopieer de openbare SSH-sleutel \"%(pubkey)s\" naar gastcomputer op afstand " -"\"%(host)s\".\n" -"Voer a.u.b. het wachtwoord in voor \"%(user)s\":" +"Modificatietijd en grootte gebruiken voor detectie van bestandswijzigingen." -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "Reservekopie maken met controlesom" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Back-up maken (modus controlegetal)" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Controlesom gebruiken om wijzigingen op te sporen" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Controlegetallen gebruiken voor het opsporen van bestandswijzigingen." -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "Maken van reservekopie pauzeren" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Back-up proces pauzeren" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "Maken van reservekopie hervatten" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Back-up proces hervatten" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "Maken van reservekopie stoppen" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Back-up proces stoppen" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Reservekopielijst vernieuwen" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Back-up lijst vernieuwen" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Naam reservekopie" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Naam van de backup" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Reservekopie verwijderen" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Back-up verwijderen" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Reservekopielogboek bekijken" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Logboek openen" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Laatste logboek bekijken" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Bekijk de log van de geselecteerde back-up." + +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Open de laatste back-up log" + +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Bekijk de laatste backuplog." -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Instellingen" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Profielen beheren…" -#: ../../qt/app.py:142 +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "user-callback bewerken" + +#: qt/app.py:532 msgid "Shutdown" msgstr "Uitschakelen" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "Computer uitschakelen wanneer de reservekopie voltooid is." +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Computer uitschakelen wanneer de back-up voltooid is." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Taal instellen…" + +#: qt/app.py:540 msgid "Exit" msgstr "Afsluiten" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Hulp" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man-pagina: Back In Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Configuratiebestand-hulp" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Toont de help pagina over Back In Time(backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Website" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man-pagina: Profielconfiguratiebestand" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Toont de help pagina over profiel configuratie bestanden(backintime-config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Open Back In Time website in de browser" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Wijzigingslogboek" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "Toon het wijzigingslogboek (lokaal als beschikbaar, anders online)" + +#: qt/app.py:573 msgid "FAQ" msgstr "FAQ" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Toon de Vaak Gestelde Vragen (FAQ) in de browser" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Een vraag stellen" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Een fout melden" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "Info" +#: qt/app.py:584 +msgid "Translation" +msgstr "Vertaling" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Omhoog" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Toont het bericht over deelname aan vertaling opnieuw." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Verborgen bestanden tonen" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Versleutelingsovergang (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Toont opnieuw het bericht over het verwijderen van EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "Info" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Herstellen" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." msgstr "" -"Herstel de geselecteerde bestanden of mappen naar het oorspronkelijke doel." +"De geselecteerde bestanden of mappen naar de oorspronkelijke bestemming " +"herstellen." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Herstellen naar..." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Herstellen naar …" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "Herstel de geselecteerde bestanden of mappen naar een nieuw doel." - -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." msgstr "" -"Herstel de momenteel getoonde map en al zijn inhoud naar het oorspronkelijke " -"doel." +"De geselecteerde bestanden of mappen naar een nieuw bestemming herstellen." -#: ../../qt/app.py:239 +#: qt/app.py:615 msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" -"Herstel de momenteel getoonde map en al zijn inhoud naar een nieuw doel." +"De momenteel weergegeven map en alle inhoud ervan naar de oorspronkelijke " +"bestemming herstellen." -#: ../../qt/app.py:249 -#, python-format +#: qt/app.py:621 msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" -"Geselecteerd bestand of map herstellen.\n" -"Wanneer deze knop grijs gedimd is komt dat hoogstwaarschijnlijk omdat " -"\"%(now)s\" is geselecteerd in de reservekopielijst aan de linkerkant.\n" -"Deze knop zal weer geactiveerd worden wanneer u op een reservekopie in deze " -"reservekopielijst klikt." +"De momenteel weergegeven map en alle inhoud ervan naar een nieuwe bestemming" +" herstellen." -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Reservekopieën" +#: qt/app.py:624 +msgid "Up" +msgstr "Omhoog" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Reservekopie" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Verborgen bestanden tonen" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Beeld" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Back-ups vergelijken…" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Snelkoppelingen" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Release Candidate" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" -"Deze map bestaat niet\n" -"in de huidige geselecteerde reservekopie!" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Toont het bericht over deze Release Candidate opnieuw." -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "Toevoegen om op te nemen" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "Toevoegen om uit te sluiten" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Back-up" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:717 +msgid "&Restore" +msgstr "Te&rugzetten" + +#: qt/app.py:723 +msgid "&Help" +msgstr "&Hulp" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Systeemvak pictogram" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automatisch" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Licht icoon" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Donker icoon" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Enkel iconen" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Enkel tekst" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Tekst onder de iconen" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Tekst naast de iconen" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" -"%(appName)s is niet geconfigureerd. Wilt u een eerdere configuratie " -"herstellen?" +"Deze map bestaat niet\n" +"in de momenteel geselecteerde back-up." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Kan reservekopiemap niet vinden.\n" -"Als deze op een extern medium staat, gelieve dit aan te sluiten en op OK te " -"drukken." +"Deze map bestaat niet\n" +"in de momenteel geselecteerde back-up." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" "Als u dit venster sluit, zal Back In Time de computer niet uit kunnen " -"schakelen wanneer de reservekopie voltooid is.\n" -"Weet u zeker dat u het wilt sluiten?" +"schakelen wanneer de back-up voltooid is." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "Bezig met:" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Het venster toch sluiten?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" -msgstr "Klaar, geen reservekopie nodig" +msgstr "Klaar, geen back-up nodig" + +#: qt/app.py:1260 +msgid "Working:" +msgstr "Bezig:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Bezig" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Fout:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Fout" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" msgstr "Verzonden:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "Snelheid:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "Geschatte aankomsttijd:" +msgstr "Geschatte afhandelingstijd:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Algemeen" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Back-up:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Hoofdmap" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "{path} terugzetten" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Persoonlijke map" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "{path} terugzetten naar …" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Mappen om reservekopie van te maken" - -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Weet u zeker dat u de volgende reservekopie wilt verwijderen?\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Hallo\n" +"U heeft Back In Time nu al een paar keer gebruikt in het {language}.\n" +"De vertaling van de geïnstalleerde versie van Back In Time in het {language} is {perc} voltooid. Ongeacht uw niveau van technische expertise kunt u een bijdrage leveren aan de vertaling en daarmee aan Back In Time zelf.\n" +"Bezoek {translation_platform_url} als u een bijdrage wilt leveren. Voor verdere hulp en vragen kunt u de {back_in_time_project_website} bezoeken.\n" +"Onze excuses voor de onderbreking. Dit bericht zal niet opnieuw worden weergegeven. Dit dialoogvenster is op elk moment beschikbaar via het hulpmenu.\n" +"Uw Back In Time-team" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "vertaalplatform" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Website" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Uw vertaling" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "In de Fediverse op Mastodon: {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "E-mail naar {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Mailinglijst {link_and_label}." -#: ../../qt/app.py:1025 -#, python-format +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} op de website van het project." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Meld een probleem" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "U kunt ook een ander kanaal naar keuze gebruiken." + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Deze versie van Back In Time is een Release Candidate en is voornamelijk bedoeld voor stabiliteitstesten ter voorbereiding op de volgende officiële release.\n" +"Er worden geen gebruikersgegevens of telemetrie verzameld. Het Back In Time-team is echter zeer geïnteresseerd in het gebruik van de Release Candidate en of het de moeite waard is om dergelijke pre-releaseversies te blijven aanbieden.\n" +"Daarom vraagt het team vriendelijk om een korte feedback over het testen van deze versie, zelfs als u geen problemen bent tegengekomen. Zelfs een korte test van een paar minuten zou ons enorm helpen.\n" +"U kunt contact met ons opnemen via de volgende contactgegevens:\n" +"{contact_list}\n" +"In deze versie wordt dit bericht niet meer weergegeven, maar u kunt het op elk moment raadplegen via het helpmenu.\n" +"Bedankt voor uw steun en voor uw hulp bij het verbeteren van Back In Time!\n" +"Uw Back In Time-team" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -"Maak reservekopie van lokale bestanden voordat u ze overschrijft of\n" -"verwijdert d.m.v. het plaatsen van '%(suffix)s' erachter." +"Er is slechts {free} vrije plaats beschikbaar op de bestemming, minder dan " +"het geconfigureerde minimum van {threshold}." -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Doorgaan met de backup?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Alle nieuwere bestanden in {path} zullen verwijderd worden. Doorgaan?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -"Nieuwere versies van bestanden zullen hernoemd worden met een '%(suffix)s' " -"erachter voordat deze hersteld worden.\n" -"Indien u deze niet meer nodig heeft kunt u ze verwijderen d.m.v. '%(cmd)s'" +"Alle recentere bestanden in de originele map zullen verwijderd worden. " +"Doorgaan?" -#: ../../qt/app.py:1038 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Let op:{BOLDEND} Het verwijderen van bestanden uit de hoofdmap van het" +" bestandssysteem kan uw hele systeem ruïneren." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "Nieuwere bestanden in oorspronkelijke map verwijderen" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Back-up naam" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Deze backup verwijderen?" +msgstr[1] "Deze backups verwijderen?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" -"Herstel de geselecteerde bestanden of mappen naar het oorspronkelijke doel\n" -"en verwijder de bestanden/mappen die niet opgenomen waren in de " -"reservekopie. Pas op:\n" -"Hierbij zullen dus de bestanden/mappen verwijderd worden die niet opgenomen " -"waren in de reservekopie!\n" -"Wees hier uiterst voorzichtig mee!!!" +"De taalinstellingen worden pas van kracht na het herstarten van Back In " +"Time." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Versie-beheerde back-ups (root)" -#: ../../qt/app.py:1083 -#, python-format +#: qt/backintime-qt-root.desktop:23 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -"Weet u zeker dat u het volgende bestand(en) wilt herstellen\n" -"naar de nieuwe map '%(path)s':" +"Gebruiksvriendelijke GUI voor versie-beheerde back-ups die het schijfgebruik" +" vermindert (root mode)" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "Weet u zeker dat u het volgende bestand(en) wilt herstellen:" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Versie-beheerde Back-ups" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -"Weet u zeker dat u alle nieuwere bestanden in '%(path)s' wilt verwijderen?" +"Gebruiksvriendelijke GUI voor versie-beheerde back-ups die het schijfgebruik" +" vermindert" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Vraag" -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" -"Weet u zeker dat u alle nieuwere bestanden in de oorspronkelijke map wilt " -"verwijderen?" +"Back-ups met achtervoegsel {suffix} maken voordat lokale elementen " +"overschreven of verwijderd worden." -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" -"WAARSCHUWING: het verwijderen van systeembestanden uit de hoofdmap kan uw " -"hele systeem ruïneren!!!" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Overzicht huidige schijfinhoud" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Reservekopie: %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Reservekopie gemaakt op %s bekijken" +"Nieuwere versies van bestanden worden hernoemd met achtervoegsel {suffix} " +"voordat ze worden teruggezet. Als u ze niet meer nodig heeft kunt u ze " +"verwijderen met het volgende commando:" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "'%s' herstellen" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Alleen elementen terugzetten die niet bestaan of nieuwer zijn dan die in de " +"bestemming. De optie \"{rsync_example}\" wordt gebruikt." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "'%s' herstellen naar..." +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Nieuwere elementen in oorspronkelijke map verwijderen." -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Schrijvers" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Geselecteerde bestanden of mappen naar de oorspronkelijke bestemming " +"herstellen en bestanden/mappen die niet in de back-up voorkomen, " +"verwijderen. Wees hier uiterst voorzichtig mee want hierdoor worden " +"bestanden/mappen verwijderd die waren uitgesloten tijdens het maken van de " +"back-up." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Weet u zeker dat u dit element wilt herstellen naar de nieuwe map?" +msgstr[1] "" +"Weet u zeker dat u deze elementen wilt herstellen naar de nieuwe map?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Weet u zeker dat u dit element wilt herstellen?" +msgstr[1] "Weet u zeker dat u deze elementen wilt herstellen?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Gebruikerscallback: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" +"Het gebruiker-callback-script moet een shebang op de eerste regel bevatten " +"(bijv. {example})." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Vertalingen" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Verborgen bestanden en mappen tonen (Ctrl+H)" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Licentie" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Toevoegen aan Opnemen" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Toevoegen aan Uitsluiten" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Selecteer uit veelgebruikte items om toe te voegen aan de uitsluitingslijst." +msgstr[1] "" +"Selecteer uit veelgebruikte items om toe te voegen aan de uitsluitingslijst." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Selecteer uit veelgebruikte items om toe te voegen aan de uitsluitingslijst." +msgstr[1] "" +"Selecteer uit veelgebruikte items om toe te voegen aan de uitsluitingslijst." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Taal instellen" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Vertaald: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Systeemstandaard" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "De taal van het besturingssysteem gebruiken." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Back-up Log Weergave" + +#: qt/logviewdialog.py:63 msgid "Last Log View" msgstr "Laatste logboekweergave" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "Reservekopielogboekweergave" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Profiel:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Back-ups:" + +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Filter:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Fout, [I] Informatie, [C] Wijziging" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "paden decoderen" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Alles" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "Fouten" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 msgid "Changes" msgstr "Wijzigingen" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informatie" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Fouten" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Fout, [I] Informatie, [C] Wijziging" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informatie" +msgstr[1] "Informatie" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "decodeerpaden" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "fouten in de rsync-overdracht (experimenteel)" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "Kopiëren" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Profielen beheren" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "Decoderen" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Bewerken" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Toevoegen" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Fout" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Verwijderen" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "Vraag" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Algemeen" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "Back InTime starten" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Opnemen" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Bezig met..." +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Uitsluiten" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Vandaag" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Verwijderen en Bewaren" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Gisteren" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "O&pties" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Deze week" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "&Geavanceerde opties" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Afgelopen week" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Configuratie herstellen" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" -"Dit is GEEN reservekopie maar een actuele weergave van uw lokale bestanden" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Nieuw profiel" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "Laatste controle %s" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Profiel hernoemen" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "Volledig logboek tonen" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Profiel \"{name}\" verwijderen?" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Bewerken" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Kopier symbolische links als bestanden" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "Aanpassen voor volledige systeemreservekopie" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." +msgstr "" +"Kopieer symbolische links als echte bestanden of mappen in de back-up. " +"Selecteer of alle links of alleen de verwijzingen buiten de bron worden " +"gekopieerd." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "Toevoegen" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Deze optie kan de back-up meer plaats doen innemen." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "Verwijderen" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Standaard niet actief." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Algemeen" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" +"Alle symbolische links worden vervangen door de echte bestanden of mappen " +"waar ze naar verwijzen. Dit vergroot het plaatsgebruik van de back-up en kan" +" hetzelfde bestand meerdere malen opslaan." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Modus:" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Gebruikt 'rsync --copy-links'." -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Alleen extern" + +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -"Waarschuwing: %(app)s gebruikt EncFS voor versleuteling. Na een " -"recente veiligheidscontrole is gebleken dat er diverse kwetsbaarheden in " -"EncFS zitten. Bekijk a.u.b. de 'Back In Time-handleiding' over 'A NOTE ON " -"SECURITY'." +"Alleen links die verwijzen naar items buiten de back-up bron worden " +"gekopieerd als bestanden. Dit vergroot het plaatsgebruik van de back-up en " +"kan hetzelfde bestand meerdere keren opslaan." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Waar wilt u de reservekopieën opslaan" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Gebruikt 'rsync --copy-unsafe-links'." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "Map" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Editor en Office tijdelijke bestanden" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "SSH-instellingen" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacs back-up bestanden" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Gastcomputer:" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs automatisch-bewaren bestanden" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Poort:" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Vim swap bestanden" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Gebruiker:" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Office tijdelijke bestanden" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Pad:" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice en andere OpenDocument Editor vergrendelingsbestanden" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "Codering:" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniaturen en Tijdelijke Afbeeldingen" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Privésleutel:" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "Miniatuur cache op GNU/Linux en andere unix-achtige OS'en" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "Sleutelbestand" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Miniatuur database op Windows" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "Maak een nieuwe SSH-sleutel aan zonder wachtwoord." +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Metadate map op MacOS" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Wachtwoord" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Applicatie-specifieke vergrendelingen" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Wachtwoord toevoegen aan sleutelbos" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discord applicatie vergrendelingsbestand" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord session vergrendelingsbestand" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Mozilla Firefox & Thunkderbird vergrendelingsbestand" + +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Caches & Tijdelijke mappen" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Gebruiker applicatie cache" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Tijdelijke map van het systeem" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Pakketcache voor Debian(-gebaseerde) GNU/Linux distributies" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Flatpak app en runtime opslag" + +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Rubtime mappen van het systeem" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Kernel en proces informatie" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Toestel en andere hardware informatie (sysfs interface)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Apparaat knooppunten" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Runtime systeembestanden" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Andere niet-blijvend" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Lijst van de actueel aangekoppelde bestandssystemen" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Systeem swapfile (virtueel geheugen)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "Gnome virtueel bestandssysteem aankoppelpunt" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Herstelde bestandssysteem objecten" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Gemengd" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Metadate map op Microsoft Windows" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Prullenmand van de gebruiker" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Back-up bestanden van het systeem" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Suggesties voor uitsluiten" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -"Wachtwoordbuffergeheugen voor Cron (Veiligheidskwestie: beheerder kan " -"wachtwoord lezen)" +"Selecteer veelgebruikte items om toe te voegen aan de back-up uitsluitingen." -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Geavanceerd" +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Standaard" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "Volledige reservekopiepad: " +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Zet terug naar voorgedefinieerde selectie" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Planning" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Dag:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" msgstr "Dag van de week:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Uur:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Tijd:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "Uren:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "na het uur" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minuten:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Back In Time uitvoeren zodra de schijf is aangesloten (slechts één keer per " +"X dagen). Er zal om het beheerderswachtwoord gevraagd worden." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" -"Laat Back In Time herhaaldelijk een reservekopie maken. Dit is vooral " -"bruikbaar wanneer de computer niet regelmatig wordt gebruikt." +"Back In Time herhaaldelijk uitvoeren. Dit is handig wanneer de computer niet" +" regelmatig wordt gebruikt." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "Elke:" +msgstr "Elk(e):" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Het loggen van foutopsporingsberichten inschakelen" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" +"Schrijft berichten van debug-gehalte naar het systeemlogboek via \"--" +"debug\"." + +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -"Laat Back In Time een reservekopie maken zodra er een extern medium " -"aangesloten wordt (alleen om de X dagen).\n" -"Er zal om uw beheerderswachtwoord gevraagd worden." +"Let op: gebruik dit alleen tijdelijk voor diagnostische doeleinden, omdat " +"het een grote hoeveelheid uitvoer genereert." + +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Uitgeschakeld" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Bij elke opstart/herstart" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Elke minuut" +msgstr[1] "Elke {n} minuten" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Elk uur" +msgstr[1] "Elke {n} uur" -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Opnemen" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Aangepaste tijdstippen" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Bestanden en mappen opnemen" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Dagelijks" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Bestand toevoegen" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Herhaaldelijk (anacron)" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Map toevoegen" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Zodra de schijf is aangesloten (udev)" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Uitsluiten" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Wekelijks" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" -"Waarschuwing: Wildcards ('foo*', '[fF]oo', 'fo?') zullen genegeerd " -"worden in de modus 'SSH-encrypted'.\n" -"Alleen ster-tekens (*) die gescheiden zijn zijn toegestaan ('foo/*', " -"'foo/**/bar')" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Maandelijks" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Jaarlijks" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Patronen, bestanden en mappen uitsluiten" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Uur/Uren" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Zeer aanbevolen:" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Dag(en)" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "Standaardwaarde toevoegen" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Week/Weken" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "Bestanden uitsluiten die groter zijn dan: " +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Maand(en)" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" -"Sluit bestanden uit die groter zijn dan de waarde in %(prefix)s.\n" -"Indien de 'Volledige rsync-modus' uitgeschakeld wordt zal dit alleen gelden " -"voor nieuwe bestanden\n" -"omdat voor rsync dit een optie is voor verplaatsen en niet voor uitsluiten.\n" -"Dus grote bestanden waar reeds een reservekopie van bestond zullen behouden " -"blijven\n" -"zelfs bij een eventuele wijziging." +"Aangepaste uren kunnen alleen een door komma's gescheiden lijst met uren " +"zijn (bijv. 8,12,18,23) of */3 voor periodieke back-ups om de 3 uur." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Automatisch verwijderen" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" +#: qt/manageprofiles/sshkeyselector.py:65 +#, fuzzy +msgid "Choose an existing private key file from somewhere else." msgstr "" +"Vel ei eksisterande privatnykelfil (normalt namngitt \"id_ed25519\" eller " +"\"id_rsa\" i eldre oppsett)." -#: ../../qt/settingsdialog.py:476 -msgid "Add default" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " +#: qt/manageprofiles/sshkeyselector.py:72 +#, fuzzy +msgid "Create a new SSH key without passphrase." +msgstr "Ny SSH lykjel i {path} kunne ikkje lagast" + +#: qt/manageprofiles/sshkeyselector.py:92 +#, fuzzy, python-brace-format +msgid "Full path: {path}" +msgstr "Full sti til augneblinksbilete:" + +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "Privat lykjel:" + +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" msgstr "" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/sshkeyselector.py:212 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Vert:" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Brukar:" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:85 +#, fuzzy +msgid "Exclude patterns, files or directories" +msgstr "Ekskluder mønster, filer eller mapper" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Ekskluder mønster" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Legg til fil" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Legg til mappe" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "Spørsmål" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Ekskluder filer større enn:" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:138 +#, fuzzy, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Ekskluder filer større enn: " -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:140 +#, fuzzy +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Ekskluder filer høgare enn verdien %(prefix)s.\n" +"Med 'full rsync modus' avslått vil kun nye filer bli handsama\n" +"fordi rsync handterer dette som eit overførings val, ikkje ei ekskludering.\n" +"Så store filer som har blitt tatt backup av før blir i snapshot\n" +"sjølv om dei har blitt endra." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Ekskluder mønster" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Ekskluder mønster" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" msgstr "" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Ekskluder fil" + +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Ekskluder mappe" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:401 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Køyr 'rsync' med '{cmd}':" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" +msgstr "Som cron jobb" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" +msgstr "på fjern vert" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +#, fuzzy +msgid "when taking a manual backup" +msgstr "Medan eit manuelt snapshot vert tatt" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +#, fuzzy +msgid "Please install 'nocache' to enable this option." +msgstr "(Ver gild å installer 'nocache' for å aktivera dette valet)" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_expert_options.py:116 +msgid "on local machine" +msgstr "På lokal maskin" + +#: qt/manageprofiles/tab_expert_options.py:130 +msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Omdiriger stdout til /dev/null i kronjobbar." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." +msgstr "Omdiriger stderr til /dev/null i kronjobbar." + +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/sek" + +#: qt/manageprofiles/tab_expert_options.py:163 +#, fuzzy +msgid "Limit rsync bandwidth usage:" +msgstr "Avgrensa rsync båndbreddebruk" + +#: qt/manageprofiles/tab_expert_options.py:204 +msgid "Preserve ACL" +msgstr "Hald på ACL" + +#: qt/manageprofiles/tab_expert_options.py:222 +msgid "Preserve extended attributes (xattr)" +msgstr "Hald på utvida attributtar (xattr)" + +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" msgstr "" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Valet må vera sitert t.d. {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "Lim inn ytterlegare val til rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." msgstr "" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format +msgid "" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Variablar må ha kontrollsekvensen \\$FOO. Dette påverkar ikkje rsync. For å " +"legge til prefiks for rsync, bruk \"{example_value}\" med " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "standardverdi" + +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Legg til prefiks til ssh kommandoer" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Sjekk om nettverksvert er på nettet" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Åtvaring: dersom deaktivert og nettverksverten ikkje er tilgjengeleg kan det" +" føre til rare feil." + +#: qt/manageprofiles/tab_expert_options.py:315 +#, fuzzy +msgid "Check if remote host supports all necessary commands." +msgstr "Sjekk om nettverksvert støttar alle naudsynte kommandoar" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Åtvaring: Om avskrudd og nettverksverten ikkje støttar alle påkrevde " +"kommandoar kan dette føre til rare feil." -#: ../../qt/settingsdialog.py:714 -msgid "on local machine" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(standardverdi: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "deaktivert" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "aktivert" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Modus:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Kvar vil du lagra snapshot" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH innstillingar" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Sti:" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "Ny profil" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Passord" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Lagra passord til lykjelring" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "Cache passord for Cron (Tryggleiksproblem: root kan lesa passord)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avansert" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +#, fuzzy +msgid "Full backup path:" +msgstr "Full sti til augneblinksbilete:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" -#: ../../qt/settingsdialog.py:721 -msgid "Redirect stdout to /dev/null in cronjobs." +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." msgstr "" -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Passordene stemmer ikkje overeins." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" msgstr "" -#: ../../qt/settingsdialog.py:774 -msgid "Preserve ACL" +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:787 -msgid "Preserve extended attributes (xattr)" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Autentiseringa av verten {host} er ikkje mogleg." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype}-nykelen sitt fingeravtrykk er:" + +#: qt/manageprofiles/tab_general.py:613 +#, fuzzy +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Verifiser dette fingeravtrykket. Vil du legge det til di 'known_hosts' fil?" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Lage ei ny kryptert mappe?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." msgstr "" -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." msgstr "" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "SSH privatlykjel" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_general.py:791 +#, fuzzy, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Ny SSH lykjel i {path} kunne ikkje lagast" + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Inkluder filer og mapper" + +#: qt/manageprofiles/tab_include.py:168 +#, fuzzy, python-brace-format msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"\"{path}\" er ein symlink. Det lenka målet vil ikkje bli tatt backup av med mindre du inkluderer det og.\n" +"Har du lyst til å inkludera symlinkmålet i staden?" -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" msgstr "" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" -msgstr "" +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Inkluder fil" + +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Inkluder mappe" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Aktiver notifikasjonar" + +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" +msgstr "Deaktiver snapshot når batteri er i bruk" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Straumstatus ikkje tilgjengeleg frå systemet" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_options.py:52 +#, fuzzy +msgid "Run only one backup at a time" +msgstr "Køyr kun eit snapshot i slengen" + +#: qt/manageprofiles/tab_options.py:56 +#, fuzzy msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Andre snapshot blir blokkerte intil der aktive snapshot er ferdig\n" +"Dette er eit globalt val. Så det vil påverka alle profilar for denne brukaren.\n" +"Du må aktivera dette for alle andre brukarar og." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Backup endrar filer under gjennoppretting" + +#: qt/manageprofiles/tab_options.py:78 +#, fuzzy +msgid "Continue on errors (keep incomplete backups)" +msgstr "Fortsett ved feil (hald på ufullstendige snapshot)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Bruk sjekksum for å oppdaga endringar" + +#: qt/manageprofiles/tab_options.py:86 +#, fuzzy +msgid "Create a new backup whether there were changes or not." +msgstr "Ta eit nytt snapshot uansett om det har vore endringar eller ei." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_options.py:122 +#, fuzzy +msgid "Log Level:" +msgstr "Loggnivå" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Ingen" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Ny profil" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Gje profilen nytt namn" +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Ikkje fjern namngitte snapshot." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Vil du verkeleg sletta profilen \"%s\"?" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "" -#: ../../qt/settingsdialog.py:1201 -msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." msgstr "" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Ikkje fjern namngitte snapshot." + +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "År" + +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Fjern augneblinksbilete" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." msgstr "" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "Kalenderveker med mondag som fyrste dag. Gjeldande veke er ignorert." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12-månaders periode. Gjeldande månad er ignorert." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Tilbakehaldsretningslinje" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Køyr i bakgrunnen på nettverksvert." + +#: qt/manageprofiles/tab_remove_retention.py:313 +#, fuzzy msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" -msgstr "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Smart fjerning vil køyre på nettverkstenaren, ikkje lokalt. Kommandoane " +"\"bash\", \"screen\" og \"flock\" må vere installerte og tilgjengeleg på " +"nettverkstenaren." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Om valt vil Back In Time fyrst teste nettverkstenaren." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dagane er talde frå og med i dag." + +#: qt/manageprofiles/tab_remove_retention.py:322 +#, fuzzy +msgid "Keep all backups for the last" +msgstr "Hald på snapshots dei siste" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "Dag(ar)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +#, fuzzy +msgid "Keep the last backup for each day for the last" +msgstr "Hald på eit augneblinksbilete per dag for dei siste" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "Vekene er talde frå og med denne veka. Ei veke startar på måndag." + +#: qt/manageprofiles/tab_remove_retention.py:347 +#, fuzzy +msgid "Keep the last backup for each week for the last" +msgstr "Hald på eit augneblinksbilete per veka for dei siste" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "Veke(r)." + +#: qt/manageprofiles/tab_remove_retention.py:357 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" -msgstr "" +"The months are counted as calendar months starting with the current month." +msgstr "Månedane er talde som kalendermånader frå og med inneverande månad." + +#: qt/manageprofiles/tab_remove_retention.py:360 +#, fuzzy +msgid "Keep the last backup for each month for the last" +msgstr "Hald på eit augneblinksbilete per månad for dei siste" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "Månad(ar)." + +#: qt/manageprofiles/tab_remove_retention.py:370 msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The years are counted as calendar years starting with the current year." msgstr "" -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Ekskluder mønster" +#: qt/manageprofiles/tab_remove_retention.py:372 +#, fuzzy +msgid "Keep the last backup for each year for" +msgstr "Hald på snapshots dei siste" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Ekskluder fil" +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Ekskluder mappe" +#: qt/manageprofiles/tab_remove_retention.py:389 +#, fuzzy +msgid "… the free space is less than" +msgstr "Viss ledig plass er mindre enn" + +#: qt/manageprofiles/tab_remove_retention.py:394 +#, fuzzy +msgid "… the free inodes are less than" +msgstr "Viss ledige inodes er mindre enn" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Fjern gamle augneblinksbilete" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Inkluder mappe" +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Snarvegar" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Vil du verkeleg endra biletmappa?" +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/placeswidget.py:97 +msgid "File System" msgstr "" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Sikkerheitskopieringsmapper" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Sjå siste logg" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Start {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Arbeider…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/restoreconfigdialog.py:134 +msgid "Import" msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Arbeider…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Dersom mappa er på ein ekstern eller fjerntilkopla disk, må den monterast " +"manuelt først." -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" msgstr "" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Vis gøymde filer" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Inkluder filer og mapper" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Oppretting av filer i denne katalogen feila:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Vis full logg" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" msgstr "" -#: ../../qt/snapshotsdialog.py:60 +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Slår av systemet etter at augneblinksbilete er ferdig." + +#: qt/shutdowndlg.py:36 +#, fuzzy +msgid "Cancel Shutdown" +msgstr "Slå av" + +#: qt/shutdowndlg.py:37 +#, fuzzy +msgid "Shutdown Now" +msgstr "Slå av" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" + +#: qt/snapshotsdialog.py:61 +#, fuzzy +msgid "Options about comparing backups" +msgstr "Val om å samanlikna snapshot" + +#: qt/snapshotsdialog.py:68 msgid "Command:" -msgstr "" +msgstr "Kommando:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" -msgstr "" +msgstr "Parametrar:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "" +msgstr "Bruk %1 og %2 for sti parametrar" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Sett ein diff-kommando eller trykk avbryt." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +#, fuzzy +msgid "Backups" +msgstr "&Sikkerheitskopier" + +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Berre forskjellige snapshot" + +#: qt/snapshotsdialog.py:159 +#, fuzzy +msgid "List only backups that are equal to:" +msgstr "List opp like snapshot til: " + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Djupsjekk (Meir eksakt, men treigare)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Slett" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Vel alle" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Samanlikna" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" +msgstr "Gå til" + +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Alternativa" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." msgstr "" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Du kan ikke samanlikna eit augneblinksbilete med seg sjølv" +#: qt/snapshotsdialog.py:470 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Vil du verkeleg sletta {file} i dette snapshot{snapshot_id}?" + +#: qt/snapshotsdialog.py:475 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Vil du verkeleg sletta {file} i {count} snapshot?" + +#: qt/snapshotsdialog.py:478 +#, fuzzy +msgid "WARNING: This cannot be revoked." +msgstr "Dette kan ikkje gjerast om!" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Fann ikkje kommandoen: %s" +#: qt/snapshotsdialog.py:495 +#, fuzzy, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Ekskluder {path} frå nye snapshot?" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Root" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "I dag" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "I går" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Denne veka" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Førre veke" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Sist sjekka {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#, fuzzy +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Dette er IKKJE eit snapshot, det er ei notids visning av dine lokale filer" diff --git a/common/po/pl.po b/common/po/pl.po index 8e4687d09..5333e96e7 100644 --- a/common/po/pl.po +++ b/common/po/pl.po @@ -1,1664 +1,2641 @@ -# Polish translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Paweł Hołuj # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2018-08-07 17:07+0000\n" -"Last-Translator: Daniel Koć \n" -"Language-Team: Polish \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-01-27 08:10+0000\n" +"Last-Translator: Merik \n" +"Language-Team: Polish \n" +"Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " -"|| n%100>=20) ? 1 : 2;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"Wszystkie licencje użyte w tym projekcie znajdują się w katalogu {dir_link}." +" Aby wyodrębnić informacje o licencjach i prawach autorskich dla " +"poszczególnych plików przy użyciu metadanych SPDX, zapoznaj się " +"z {readme_link}." -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Profil \"%s\" już istnieje!" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Nie możesz usunąć ostatniego profilu!" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Nieaktywny" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Przy każdym uruchomieniu / restarcie" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Co 5 minut" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Ostrzeżenie" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Co 10 minut" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Profil główny" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Co 30 minut" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Lokalny (zaszyfrowane EncFS)" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Co godzinę" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (zaszyfrowane EncFS)" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Co 2 godziny" +#: common/config.py:237 +msgid "Local" +msgstr "Lokalny" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Co 4 godziny" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Zaszyfrowany lokalnie" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Co 6 godzin" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Szyfrowanie" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Co 12 godzin" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Własny harmonogram" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Prywatny klucz SSH" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Codziennie" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil: „{name}”" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Wielokrotnie (anacron)" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Katalog kopii zapasowych jest nieprawidłowy." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Po podłączeniu dysku (udev)" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "" +"Należy wybrać co najmniej jeden katalog do utworzenia kopii zapasowej." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Co tydzień" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Katalog: {path}" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Co miesiąc" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "" +"Tego katalogu nie można uwzględnić w kopii zapasowej, ponieważ jest on " +"częścią miejsca docelowego kopii zapasowej." -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "dni" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" +"Wartość parametru „Usuń najstarszą kopię zapasową, jeśli ilość wolnego " +"miejsca jest mniejsza niż” ({val_one}) musi być mniejsza lub równa progowi " +"parametru „Ostrzegaj, jeśli ilość wolnego miejsca na dysku spadnie poniżej” " +"({val_two})." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "tygodni" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" +"Dostosuj ustawienia tak, aby limit usuwania kopii zapasowych nie był wyższy " +"niż limit ostrzegawczy." -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "lat" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Harmonogram udev nie działa z trybem {mode}" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "godzin" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Nie udało się zapisać nowej tablicy zadań (crontab)." -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "miesięcy" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron nie działa pomimo dostępnego polecenia crontab. Zaplanowane zadania " +"tworzenia kopii zapasowych nie będą działać. Cron może być zainstalowany, " +"ale nie jest włączony. Spróbuj uruchomić dwa polecenia: „systemctl enable " +"cron” i „systemctl start cron” lub skorzystaj z kanałów wsparcia aktualnie " +"używanej dystrybucji GNU/Linux, aby uzyskać pomoc." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Nie udało się zapisać konfiguracji" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Nie udało się wczytać konfiguracji" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Profil „{name}” już istnieje." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Ostatniego profilu nie można usunąć." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Nie można zamontować „{command}”" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Nie znaleziono konfiguracji dla zaszyfrowanego katalogu." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Czy stworzyć nowy zaszyfrowany katalog?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Anuluj" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " EKSPERYMENTALNE!" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Ponownie wpisz hasło EncFS, aby potwierdzić." -#: ../../common/config.py:129 -msgid "Local" -msgstr "lokalnie" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "Hasła EncFS nie są takie same." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Utwórz migawkę" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Klucz prywatny SSH" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Nie można zainicjować zaszyfrowanej ścieżki „{command}”" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "lokalnie z szyfrowaniem" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Nie można odmontować {mountprocess} z {mountpoint}." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Szyfrowanie" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"Nie znaleziono {command}. Zainstaluj to (np. poprzez „{installcommand}”)" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH z szyfrowaniem" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Punkt montowania {mntpoint} nie jest pusty." -#: ../../common/config.py:135 -msgid "Default" -msgstr "Domyślny" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Wpisz hasło do profilu „{profile}” {mode}:" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Nie można zainstalować reguły Udev dla profilu {profile_id}. Usługa DBus " +"„{dbus_interface}” była niedostępna." -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Nie udało się odnaleźć UUID dla {path}" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "NIEPOWODZENIE" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Przywracanie uprawnień" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Gotowe" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Następujące wpisy z listy uwzględnionych nie mają odpowiadającego im pliku " +"lub katalogu w źródle kopii zapasowej:" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Kopia zapasowa przełożona z powodu pracy na baterii" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Nie można odnaleźć katalogu kopii zapasowych." -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Jeśli znajduje się na dysku wymiennym, podłącz go." -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Pozostała {n} sekunda." +msgstr[1] "Pozostały {n} sekundy." +msgstr[2] "Pozostało {n} sekund." -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Nie udało się utworzyć kopii zapasowej {snapshot_id}." -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Prosimy o cierpliwość. Finalizowanie…" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Profil główny" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Nie można utworzyć katalogu." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil: \"%s\"" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Zapisywanie pliku konfiguracyjnego…" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Katalog migawek jest nieprawidłowy!" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Zapisywanie uprawnień…" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." msgstr "" -"Musisz wybrać co najmniej jeden katalog do wykonania kopii zapasowej!" +"Znaleziono niekompletną kopię zapasową {snapshot_id}, która może być " +"kontynuowana." -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Nie możesz dołączyć katalogu z kopią zapasową!" - -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Nie można dołączyć podkatalogu backup!" - -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s nie jest katalogiem!" - -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Pola Host, Użytkownik i Profil nie mogą być puste!" - -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" msgstr "" -"Nie mogę zapisać do: %s\n" -"Jesteś pewien, że posiadasz uprawnienia do jego zapisu?" +"Usuwanie niekompletnego katalogu {snapshot_id} z ostatniego uruchomienia" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Nie mogę usunąć katalogu" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Tworzenie kopii zapasowej" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Kopiowanie linków (odwołanie linków symbolicznych)" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Sukces" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Opcje zaawansowane" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Transfer częściowy z powodu błędu" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" +"Transfer częściowy z powodu zaginionych plików źródłowych (patrz „man " +"rsync”)" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Nie można odnaleźć tablicy cron.\n" -"Czy cron jest zainstalowany ?\n" -"Jeżeli nie, wszystkie automatyczne kopie zapasowe powinny zostać wyłączone." +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "„rsync” zakończył się kodem wyjścia {exit_code}" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Więcej informacji można znaleźć w „man rsync”" -#: ../../common/config.py:1575 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" +"Negatywne kody wyjściowe rsync to liczby sygnałów, patrz „kill -l” i „man " +"kill”" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Nic się nie zmieniło, nie jest konieczna żadna nowa kopia zapasowa" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Nie można zmienić nazwy z {new_path} na {path}." -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Stosowanie reguł usuwania starych kopii zapasowych" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Stosowanie zasad przechowywania" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Próba utrzymamia minimum wolnego miejsca" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Anuluj" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Próba utrzymania minimum {perc} wolnych i-węzłów" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Potwierdź hasło" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Teraz" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Hasło niezgodne" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Nie można zamontować {sshfs}" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." msgstr "" -"encfs w wersji 1.7.2 i wcześniejsze miały bład z opcją --reverse. " -"Zaktualizuj encfs" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Utwórz migawkę" +"Nie znaleziono programu ssh-agent. Upewnij się, że jest zainstalowany." -#: ../../common/mount.py:415 -#, python-format +#: common/sshtools.py:489 msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" - -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"Nie można odblokować klucza prywatnego SSH. Hasło niewłaściwe lub " +"niedostępne dla cron." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Zdalna ścieżka istnieje, ale nie jest katalogiem." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Ścieżka zdalna nie jest zapisywalna." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Ścieżka zdalna nie jest wykonywalna." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Profil '%(profile)s': podaj hasło do %(mode)s: " +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Nie można utworzyć zdalnej ścieżki." -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Zdalny host {host} nie obsługuje {command}" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "NIEPOWODZENIE" +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Sprawdzanie poleceń na hoście {host} zwróciło nieznany błąd" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Przywracanie uprawnień:" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Zdalny host {host} nie obsługuje dowiązań twardych" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Gotowe" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Skopiuj klucz publiczny SSH „{pubkey}” do zdalnego hosta „{host}”." -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Wpisz hasło dla „{user}”." -#: ../../common/snapshots.py:669 +#: common/tools.py:382 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"Nie można odnaleźć katalogu migawek.\n" -"Jeżeli katalog znajduje się na dysku wymiennym proszę go podłączyć." - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Pozostała %s sekunda." -msgstr[1] "Pozostały %s sekundy." -msgstr[2] "Pozostało %s sekund." +"Docelowy system plików dla {path} jest sformatowany za pomocą NTFS, w którym" +" występują znane niezgodności z systemami plików w stylu uniksowym." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Nie udało się wykonać migawki %s!!!" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} nie jest prawidłowym katalogiem." -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Finalizuję" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Utworzenie następującego katalogu nie powiodło się:" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Nie mogę stworzyć katalogu: %s" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Dostęp do zapisu może być ograniczony." -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Zapisz plik konfiguracyjny ..." - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Zapis uprawnień ..." - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "…" - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"Docelowy system plików dla {path} jest sformatowany za pomocą FAT, który nie" +" wspiera dowiązań twardych. Użyj natywnego systemu plików GNU/Linux." -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"Docelowy system plików dla {path} to udział zamontowany za pomocą SMB. " +"Upewnij się, że zdalny serwer SMB wspiera dowiązania symboliczne lub aktywuj" +" „{copyLinks}” w „{expertOptions}”." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Nie mogę usunąć katalogu: %s" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Kopiowanie dowiązań (odwołanie dowiązań symbolicznych)" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Utwórz migawkę" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Opcje zaawansowane" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"Docelowy system plików dla {path} to udział zamontowany za pomocą sshfs, " +"który nie wspiera dowiązań twardych. Użyj trybu „SSH”." -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Tworzenie pliku w tym katalogu nie powiodło się:" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Inteligentne usuwanie" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Informacje o Back In Time" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Usuwanie poprzednich migawek" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Prawo autorskie:" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Spróbuj utrzymać minimalną ilość wolnego miejsca" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Autorzy:" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Tłumacze:" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "Z BŁĘDAMI!" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Paweł Hołuj " -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Teraz" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Informacje o tłumaczach w bieżącym języku są niedostępne." -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "to łącze" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" +"Kliknij {thislink}, aby uzyskać informacje o tłumaczach wszystkich języków." -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Strona internetowa projektu" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Instrukcja obsługi" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Otwórz instrukcję obsługi w przeglądarce (lokalnie, jeśli jest dostępna, " +"w przeciwnym razie online)" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Wersja{BOLDEND}: {version}" -#: ../../common/sshtools.py:470 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"{app_name} prawdopodobnie działa po raz pierwszy, ponieważ nie znaleziono " +"żadnej konfiguracji." -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:337 msgid "" -"Remote path is not executable:\n" -" %s" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Czy zaimportować istniejącą konfigurację z kopii zapasowej lub innego " +"komputera?" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Następnie naciśnij OK." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Utwórz kopię zapasową" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Użyj czasu i rozmiaru modyfikacji do wykrywania zmian w pliku." -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Utwórz kopię zapasową (tryb sumy kontrolnej)" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Używaj sum kontrolnych do wykrywania zmian plików." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Wstrzymaj tworzenie kopii zapasowej" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "Migawka z sumami kontrolnymi" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Wznów tworzenie kopii zapasowej" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Używaj sum kontrolnych do wykrywania zmian" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Zatrzymaj tworzenie kopii zapasowej" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "Wstrzymaj wykonywanie migawki" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Odśwież listę kopii zapasowych" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "Wznów wykonywanie migawki" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Nazwij kopię zapasową" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "Zakończ wykonywanie migawki" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Usuń kopię zapasową" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Odśwież listę migawek" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Otwórz dziennik kopii zapasowej" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Nazwa migawki" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Wyświetl dziennik wybranej kopii zapasowej." -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Usuń migawkę" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Otwórz dziennik ostatniej kopii zapasowej" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Podgląd dziennika migawki" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Wyświetl dziennik ostatniej kopii zapasowej." -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Podgląd ostatniego dziennika" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Zarządzaj profilami…" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Ustawienia" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Edytuj user-callback" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" -msgstr "Zamykanie" +msgstr "Zamknięcie" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "Zamknij system po wykonaniu migawki" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Zamknij system po zakończeniu tworzenia kopii zapasowej." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Ustaw język…" + +#: qt/app.py:540 msgid "Exit" msgstr "Wyjście" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Pomoc" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man page: Back In Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Podręcznik pliku konfiguracji" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Wyświetla man page dotyczącą Back In Time (backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Strona WWW" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man page: Plik konfiguracyjny profili" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Wyświetla man page dotyczącą pliku konfiguracyjnego profili (backintime-" +"config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Otwórz witrynę Back In Time w przeglądarce" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Lista zmian" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" +"Otwórz dziennik zmian (lokalnie, jeśli jest dostępny, w przeciwnym razie " +"online)" + +#: qt/app.py:573 msgid "FAQ" msgstr "FAQ" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Otwórz często zadawane pytania (FAQ) w przeglądarce" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Zadaj pytanie" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Zgłoś błąd" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "O programie" +#: qt/app.py:584 +msgid "Translation" +msgstr "Tłumaczenie" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Do góry" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Ponownie pokazuje komunikat o uczestnictwie w tłumaczeniu." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Pokaż ukryte pliki" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Przejście szyfrowania (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Ponownie pokazuje komunikat o usuwaniu EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "O programie" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Przywróć" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "Przywróć wybrane pliki lub katalogi do pierwotnej lokalizacji." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Przywróć do ..." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Przywróć do…" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "Przywróć wybrane pliki lub katalogi do nowej lokalizacji." -#: ../../qt/app.py:234 +#: qt/app.py:615 msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Przywróć aktualnie wyświetlany katalog i całą jego zawartość do pierwotnej " +"lokalizacji." -#: ../../qt/app.py:239 +#: qt/app.py:621 msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" +"Przywróć aktualnie wyświetlany katalog i całą jego zawartość do nowej " +"lokalizacji." -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "Do góry" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Migawki" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Pokaż ukryte pliki" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Migawka" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Porównaj kopie zapasowe…" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Widok" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Wydanie kandydujące" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Skróty" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Wyświetla ponownie komunikat o wydaniu kandydującym." -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Kopia zapasowa" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Przywróć" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:723 +msgid "&Help" +msgstr "P&omoc" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Ikona zasobnika systemowego" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "Automatycznie" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Jasna ikona" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Ciemna ikona" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Tylko ikony" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Tylko tekst" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Tekst pod ikonami" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Tekst obok ikony" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Ten katalog nie istnieje\n" +"w wybranej kopii zapasowej." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Nie można odnaleźć katalogu migawek.\n" -"Jeżeli katalog znajduje się na dysku wymiennym proszę go podłączyć i wcisnąć " -"OK" +"Ten katalog nie istnieje\n" +"w wybranej kopii zapasowej." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Jeśli to okno będzie zamknięte, Back In Time nie będzie mógł zamknąć systemu" +" po zakończeniu tworzenia kopii zapasowej." -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "Pracuje:" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Zamknąć okno mimo wszystko?" -#: ../../qt/app.py:711 +#: qt/app.py:1189 msgid "Done, no backup needed" msgstr "Gotowe, archiwizacja niepotrzebna" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Błąd:" +#: qt/app.py:1260 +msgid "Working:" +msgstr "Działanie:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Działanie" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Błąd" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" msgstr "Wysłane:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "Prędkość:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "Do końca:" +msgstr "Czas do końca:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Globalne" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Kopia zapasowa:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Root" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Przywróć {path}" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Start" - -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Katalogi kopii zapasowej" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Przywróć {path} do…" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Czy na pewno usunąć migawkę:\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Witaj!\n" +"Back In Time w języku {language} został użyty już kilka razy.\n" +"Tłumaczenie zainstalowanej wersji Back In Time na język {language} jest kompletne w {perc}. Możesz pomóc w ulepszeniu tłumaczeń, a poprzez to w ulepszaniu samego Back In Time bez posiadania żadnej wiedzy technicznej.\n" +"Odwiedź {translation_platform_url}, jeśli chcesz pomóc. W celu uzyskania odpowiedzi na pytania lub dalszych wskazówek odwiedź {back_in_time_project_website}.\n" +"Przepraszamy za utrudniania, ten komunikat nie pojawi się sam ponownie, ale będzie dostępny w menu pomocy.\n" +"Zespół Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "platformę do tłumaczenia" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Strona WWW" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Twoje tłumaczenia" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "W Fediverse na platformie Mastodon: {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Wyślij wiadomość e-mail na adres {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Lista mailingowa {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} na stronie internetowej projektu." -#: ../../qt/app.py:1025 -#, python-format +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Otwórz zagadnienie" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Możesz również użyć innego wybranego kanału." + +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Ta wersja Back In Time jest wydaniem kandydującym i jest przeznaczona głównie do testów stabilności w przygotowaniu do kolejnej oficjalnej wersji.\n" +"Nie są gromadzone żadne dane użytkowników ani dane telemetryczne. Jednak zespół Back In Time jest bardzo zainteresowany tym, czy wydanie kandydujące jest używane i czy warto nadal udostępniać takie wersje przedpremierowe.\n" +"Dlatego zespół uprzejmie prosi użytkowników o krótką opinię na temat tego, czy ta wersja była przez nich testowana, nawet jeśli nie napotkali żadnych problemów. Zaledwie krótki test trwający kilka minut bardzo by nam pomógł.\n" +"Dostępne są następujące opcje kontaktu:\n" +"{contact_list}\n" +"W tej wersji ten komunikat nie zostanie ponownie wyświetlony, ale można uzyskać do niego dostęp w dowolnym momencie za pośrednictwem menu pomocy.\n" +"Dziękujemy za wsparcie i pomoc w ulepszaniu Back In Time!\n" +"Twój zespół Back In Time" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Dostępne jest tylko {free} wolnego miejsca w miejscu docelowym, co jest " +"poniżej skonfigurowanego progu {threshold}." -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Kontynuować tworzenie kopii zapasowej?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Wszystkie nowsze pliki w  {path} zostaną usunięte. Kontynuować?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Wszystkie nowsze pliki w oryginalnym katalogu zostaną usunięte. Kontynuować?" -#: ../../qt/app.py:1038 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Ostrzeżenie{BOLDEND}: usunięcie plików z katalogu głównego systemu " +"plików może spowodować uszkodzenie całego systemu." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Nazwa kopii zapasowej" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Usunąć tę kopię zapasową?" +msgstr[1] "Usunąć te kopie zapasowe?" +msgstr[2] "Usunąć te kopie zapasowe?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" +"Ustawienia języka zaczną obowiązywać dopiero po ponownym uruchomieniu " +"programu Back In Time." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Wersjonowane kopie zapasowe (root)" -#: ../../qt/app.py:1083 -#, python-format +#: qt/backintime-qt-root.desktop:23 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"Łatwy w obsłudze interfejs do tworzenia wersjonowanych kopii zapasowych " +"(root)" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Wersjonowane kopie zapasowe" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Łatwy w obsłudze interfejs do tworzenia wersjonowanych kopii zapasowych" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Pytanie" -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Utwórz kopie zapasowe z końcowym {suffix} przed nadpisaniem lub usunięciem " +"elementów lokalnych." -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Przed przywróceniem nowsze wersje plików zostaną przemianowane z dodanym " +"{suffix}. Te pliki można usunąć za pomocą następującego polecenia:" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Pokaż obecną zawartość dysku" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Migawka: %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Zobacz migawkę wykonaną %s" - -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Przywróć '%s'" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Przywróć tylko te elementy, które nie istnieją lub są nowsze niż te " +"w miejscu docelowym. Korzystanie z opcji „{rsync_example}”." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Przywróć '%s' do ..." +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Usuń nowsze elementy z pierwotnego katalogu." -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Autorzy" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Przywróć wybrane pliki lub katalogi do oryginalnego miejsca docelowego " +"i usuń pliki lub katalogi, których nie ma w kopii zapasowej. Zachowaj " +"szczególną ostrożność, ponieważ spowoduje to usunięcie plików i katalogów, " +"które zostały wykluczone podczas tworzenia kopii zapasowej." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Czy na pewno przywrócić ten element do nowego katalogu?" +msgstr[1] "Czy na pewno przywrócić te elementy do nowego katalogu?" +msgstr[2] "Czy na pewno przywrócić te elementy do nowego katalogu?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Czy na pewno przywrócić ten element?" +msgstr[1] "Czy na pewno przywrócić te elementy?" +msgstr[2] "Czy na pewno przywrócić te elementy?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Wywołanie zwrotne użytkownika: „{filename}”" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" +"Skrypt wywołania zwrotnego użytkownika musi zawierać shebang w pierwszym " +"wierszu (np. {example})." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Tłumaczenia" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Pokaż/ukryj ukryte pliki i katalogi (Ctrl+H)" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Licencja" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Dodaj do uwzględnionych" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Dodaj do wykluczonych" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Wybierz z listy często używanych elementów, które chcesz dodać do listy " +"wykluczeń." +msgstr[1] "" +"Wybierz z listy często używanych elementów, które chcesz dodać do listy " +"wykluczeń." +msgstr[2] "" +"Wybierz z listy często używanych elementów, które chcesz dodać do listy " +"wykluczeń." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Wybierz z listy często używanych elementów, które chcesz dodać do listy " +"wykluczeń." +msgstr[1] "" +"Wybierz z listy często używanych elementów, które chcesz dodać do listy " +"wykluczeń." +msgstr[2] "" +"Wybierz z listy często używanych elementów, które chcesz dodać do listy " +"wykluczeń." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Ustaw język" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Przetłumaczono: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Domyślne systemowe" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Użyj języka systemu operacyjnego." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Widok dziennika kopii zapasowej" + +#: qt/logviewdialog.py:63 msgid "Last Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +msgstr "Ostatni widok dziennika" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Profil:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Kopie zapasowe:" + +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Filtr:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "wszystkie" +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] błąd, [I] informacja, [C] zmiana" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "błędy" +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "dekoduj ścieżki" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "wszystkie" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 msgid "Changes" msgstr "zmiany" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "informacje" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "błędy" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] błąd, [I] informacja, [C] zmiana" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informacja" +msgstr[1] "Informacje" +msgstr[2] "Informacji" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "błędy transferu rsync (eksperymentalne)" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "Kopiuj" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Zarządzaj profilami" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Edycja" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Dodaj" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Błąd" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Usuń" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "Pytanie" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Ogólne" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "Uruchom BackInTime" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Dołącz" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Pracuję..." +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Wyklucz" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Dziś" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Usuń i &przechowuj" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Wczoraj" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Ustawienia" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Ten tydzień" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "Opcje e&ksperta" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Poprzedni tydzień" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Przywróć konfigurację" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Nowy profil" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Zmień nazwę profilu" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Usuń profil „{name}”?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Kopiuj dowiązania symboliczne jako pliki" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Kopiuj dowiązania symboliczne jako rzeczywiste pliki lub katalogi w kopii " +"zapasowej. Wybierz, czy mają zostać skopiowane wszystkie dowiązania, czy " +"tylko te wskazujące na zewnątrz źródła." + +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Ta opcja może zwiększyć rozmiar kopii zapasowej." + +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Domyślnie wyłączona." -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Wszystkie dowiązania symboliczne są zastępowane rzeczywistymi plikami lub " +"katalogami, do których wskazują. Zwiększa to rozmiar kopii zapasowej i może " +"powodować wielokrotne zapisywanie tych samych plików." -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Używa „rsync --copy-links”." + +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Tylko zewnętrzne" + +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"Tylko dowiązania prowadzące poza źródło kopii zapasowej są kopiowane jako " +"pliki. Zwiększa to rozmiar kopii zapasowej i może powodować wielokrotne " +"zapisywanie tych samych plików." -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Edycja" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Używa „rsync --copy-unsafe-links”." -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "Ustaw pełną kopię systemu" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Pliki tymczasowe edytora i pakietu biurowego" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "Dodaj" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Pliki kopii zapasowej Emacsa" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "Usuń" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Pliki automatycznego zapisywania Emacsa" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Ogólne" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Pliki wymiany Vima" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Tryb:" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Pliki tymczasowe pakietu Microsoft Office" -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "Pliki blokady LibreOffice i innych edytorów OpenDocument" + +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Miniatury i zdjęcia tymczasowe" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" +"Pamięć podręczna miniatur w systemach GNU/Linux i innych systemach " +"uniksopodobnych" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Gdzie zapisać migawki" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Baza miniatur w systemie Windows" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "Katalog" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Katalog metadanych w systemie MacOS" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "Ustawienia SSH" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Blokady specyficzne dla aplikacji" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Host:" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Plik blokady aplikacji Discord" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Port:" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Plik blokady sesji aplikacji Discord" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Użytkownik:" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Plik blokady programów Mozilla Firefox i  Thunderbird" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Ścieżka:" +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Pamięci podręczne i katalogi tymczasowe" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "Szyfr:" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Pamięć podręczna aplikacji użytkownika" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Klucz prywatny:" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Tymczasowy katalog systemowy" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "Plik klucza" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Pamięć podręczna pakietów dystrybucji GNU/Linux opartych na Debianie" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "Utwórz nowy klucz SSH bez hasła" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Repozytorium aplikacji i środowiska wykonawczego Flatpak" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Hasło" +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Katalogi środowiska wykonawczego systemu" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Zapisz hasło w portfelu" +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Informacje o jądrze i procesach" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Informacje o urządzeniu i innym sprzęcie (interfejs sysfs)" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Zaawansowane" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Węzły urządzeń" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Pliki systemowe środowiska wykonawczego" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Inne nietrwałe" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Lista aktualnie zamontowanych systemów plików" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Plik wymiany systemu (pamięć wirtualna)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "Punkt montowania wirtualnego systemu plików w środowisku GNOME" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Odzyskane obiekty systemu plików" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Różne" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Katalog metadanych w systemie Microsoft Windows" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Kosz użytkownika" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Pliki kopii zapasowej systemu" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Wyklucz sugestie" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" +"Wybierz często używane elementy, które chcesz dodać do wykluczeń kopii " +"zapasowej." + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Domyślne" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Resetuj do wstępnie zdefiniowanego wyboru" + +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Harmonogram" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Dzień:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" msgstr "Dzień powszedni:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Godzina:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Czas:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "Godziny:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "po godzinie" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minuty:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Uruchom Back In Time, gdy tylko dysk zostanie podłączony (tylko raz co X " +"dni). Pojawi się monit o hasło sudo." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Uruchamiaj Back In Time wielokrotnie. Jest to przydatne, jeśli komputer nie " +"działa regularnie." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" -msgstr "Co" +msgstr "Co:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Włącz rejestrowanie komunikatów debugowania" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" +"Zapisuje komunikaty poziomu debugowania w dzienniku systemowym poprzez " +"„--debug”." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Uwaga: używaj tego tylko tymczasowo do diagnostyki, ponieważ generuje to " +"dużą ilość danych wyjściowych." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Dołącz" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Nieaktywny" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Dołącz pliki i katalogi" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Przy każdym uruchomieniu / restarcie" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Dodaj plik" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Co minutę" +msgstr[1] "Co {n} minuty" +msgstr[2] "Co {n} minut" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Co godzinę" +msgstr[1] "Co {n} godziny" +msgstr[2] "Co {n} godzin" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Dodaj katalog" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Własny harmonogram" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Wyklucz" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Codziennie" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Wielokrotnie (anacron)" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Wyklucz wzorce, pliki lub katalogi" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Po podłączeniu dysku (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Co tydzień" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Bardzo zalecane:" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Co miesiąc" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "Dodaj domyślne" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Co rok" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "godzin" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "dni" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "tygodni" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "miesięcy" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Godziny niestandardowe mogą być jedynie listą godzin oddzielonych " +"przecinkami (np. 8,12,18,23) lub */3 w przypadku okresowych kopii zapasowych" +" co 3 godziny." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Automatyczne usuwanie" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Mais antigo do que:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Escolher uma chave privada existente de outro local." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Se espaço livre é inferior a:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Falha ao criar chave SSH sem senha." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Caminho completo: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Chave Privada:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Usar configuração SSH do sistema" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Deixa o ficheiro de chave desselecionado. Conexões SSH usarão a configuração" +" do cliente existente do sistema (e.g., ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Proxy SSH" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Servidor:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Porta:" + +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Usuário:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Ligar ao servidor de destino através deste proxy (também conhecido com " +"servidor de salto). Ver \"-J\" na documentação do comando \"ssh\" ou " +"\"ProxyJump\" em \"ssh_config\" para maiores detalhes." -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" +"{BOLD}Informação{ENDBOLD}: no modo \"encriptado por SSH\", apenas funcionam " +"asteriscos simples ou duplos (exemplo, {example2}). Outros tipos de " +"caracteres especiais e padrões serão ignorados (exemplo, {example1}). Os " +"nomes de arquivos são imprevisíveis neste modo devido à encriptação por " +"EncFS." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Não remover snapshots nomeadas" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Excluir padrões, arquivos ou pastas" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opções" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Adicionar padrão" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Activar as notificações" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Adicionar ficheiros" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Adicionar pastas" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Sugestões" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" +"Selecione dos items usados frequementemente para o adicionar à lista de " +"exclusão." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Excluir arquivos maiores que:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Excluir arquivos maiores que o valor em {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." +msgstr "" +"Com o 'modo rsync completo' desativado, isto apenas afetará os novos " +"arquivos, uma vez que para o rsync esta é uma opção de transferência, não " +"uma opção de exclusão. Portanto, arquivos grandes que se encontram presentes" +" em cópias de segurança antigas irão persistir nas cópias, mesmo que tenham " +"sido modificados." + +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Excluir padrão" -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Insira um padrão de exclusão:" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Para ajuda, use a página de ‘man’ secção {link}." -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Abrir página de man rsync" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Excluir arquivos" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Excluir diretórios" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Alterações & Erros" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "Desativado porque este padrão não funciona no modo 'SSH encriptado'." -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Estas opções dão para configurações avançadas. Modifique somente o que você " +"tiver conhecimento dos resultados." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Execute 'rsync' com '{cmd}':" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "como cron job" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" +msgstr "no servidor remoto" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "ao tirar uma cópia manual" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Por favor instale 'nocache' para ativar esta opção." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" - -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "na máquina local" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Redirecione stdout para /dev/null em cronjobs." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"O Cron enviará automaticamente um e-mail com a saída anexada dos cronjobs se" +" estiver instalado um MTA." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Redirecione stderr para /dev/null em cronjobs." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"O Cron enviará automaticamente um e-mail com erros anexados de cronjobs se " +"um MTA estiver instalado." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/seg" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Limite a utilização da largura de banda rsync:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "" +msgstr "Preservar ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "" +msgstr "Preservar atributos estendidos (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Restringir a um sistema de arquivos" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "As opções devem ser citadas, p. {example}." -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Cole opções adicionais para rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefixo a ser executado antes de cada comando no servidor remoto." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"As variáveis precisam de ser escapadas com \\$FOO. Isto não afeta o rsync. " +"Assim, para adicionar um prefixo ao rsync, utilize \"{example_value}\" com " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "padrão" + +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" -msgstr "" +msgstr "Adicionar prefixo aos comandos SSH" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Verificar se o servidor remoto está online" + +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Aviso: Se estiver desativado e o servidor remoto não estiver disponível, " +"isto poderá levar a alguns erros estranhos." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Verifique se o servidor remoto suporta todos os comandos necessários." + +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Aviso: Se estiver desativado e o servidor remoto não suportar todos os " +"comandos necessários, poderá levar a alguns erros estranhos." -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(padrão: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "desativado" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "ativado" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Modo:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Onde guardar as cópias de segurança" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Definições de SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Caminho:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Ficheiro da chave:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Senha" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Guardar a Senha em um Keyring" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Senha de Cache para Cron (problema de segurança: o root consegue ler a " +"senha)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avançado" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Caminho completo da cópia:" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" +"A planificação está desativada porque nenhuma instalação do cron foi " +"encontrado. Por favor, instale cron para criar cópias de segurança " +"planificado." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "O caminho de destino da cópia não pode estar vazio." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "A password de encriptação não pode estar vazia." + +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"Um erro ocorreu ao tentar entrar no servidor remoto. A seguinte mensagem foi" +" recebida:" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Para ativar login sem password, a chave de SSH pública pode ser copiada para" +" o host remoto." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Proceder a copiar a chave de SSH?" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Não foi possível copiar a chave pública de SSH. Isto poderá ser devido a um " +"problema de conexão ou permissão." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Novo perfil" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "A autenticidade do servidor {host} não pode ser verificada." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Renomear perfil" +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "A impressão digital da chave {keytype} é:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "" +"Verifique esta impressão digital. Gostaria de adicioná-la ao arquivo de " +"'known_hosts'?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Mudar mesmo a pasta de cópias de segurança?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "O destino de cópia selecionado não está vazio." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Precisa de estar vazio para usar encriptação." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Tem a certeza que desja remover este perfil \"%s\" ?" +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Ficheiro inválido: Não é uma chave SSH privada" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"O ficheiro selecionado ({path}) parece ser uma chave de SSH pública. Por " +"favor, selecione um ficheiro de chave privada (sem uma extensão\".pub\")." -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" +"O ficheiro {path} já existe. Impossível criar uma chave SSH com esse nome." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Falha ao criar nova chave SSH em {path}." + +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Incluir arquivos e pastas" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"\"{path}\" é um link simbólico (symlink). Não será feito uma cópia de " +"segurança do destino selecionado até o incluir, também." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Incluir o alvo do link simbólico (symlink)?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Incluir ficheiros" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Incluir pastas" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Ativar notificações" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Desativar cópias quando usando a bateria" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Estado de energia não disponível no sistema" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Executar uma cópia de cada vez" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Outras cópias de segurança serão bloqueadas até que a cópia atual esteja " +"concluída. Esta é uma opção global. Portanto, isto afetará todos os perfis " +"deste utilizador. Mas também precisará ativar isto para todos os outros " +"utilizadores." -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Cópia de segurança dos arquivos substituídos na restauração" + +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Continuar com erros (manter cópias incompletas)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Use a soma de verificação para detectar mudanças" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Criar uma cópia independentemente de haver alterações ou não." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Avisar se o espaço de disco é reduzido abaixo" + +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Mostra um aviso quando o espaço livre no destinatário de cópias de segurança" +" é inferior ao valor especificado." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Se a política de Remoção & Retenção estiver ativada e cópias de segurança " +"antigas estiverem a ser removidas conforme o espaço livre disponível, este " +"valor não pode ser inferior do que o valor estabelecido na política." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Excluir ficheiro" +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Nível de Registro:" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Excluir pasta" +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nenhum" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "manual do usuário" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"As seguintes regras foram processadas de cima para baixo. As últimas regras " +"substituem as anteriores. Verifique o {manual_link} para detalhes e " +"exemplos." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Incluir pasta" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Abrir o manual do usuário no navegador." -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Manter a cópia mais recente." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." msgstr "" +"A cópia de segurança mais recente é mantida sob quaisquer circunstâncias." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "O comportamento não será alterado." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Manter as cópias nomeadas." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"As cópias de segurança que possuírem um nome dado em adição ao carimbo de " +"data/hora padrão, serão mantidas sob quaisquer circunstâncias e não serão " +"removidas." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Ano(s)" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Remover cópias mais antigas que" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Dias completos. O dia atual será ignorado." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "Semanas iniciadas com Segunda-feira. A semana atual sera ignorada." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Períodos de 12 meses. O mês atual será ignorado." -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Política de retenção" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Executar em segundo plano no servidor remoto." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"O procedimento de remoção inteligente irá rodar diretamente no servidor, não" +" localmente. Os comandos \"bash\", \"screen\"e \"flock\" precisam estar " +"instalados e disponíveis no servidor remoto." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Se selecionado, Back In Time primeiramente testará o servidor remoto." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dias contados começando a partir de hoje." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Mantenha todas as cópias pelo(s) último(s)" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "dia(s)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Mantenha uma cópia para cada dia pelo(s) último(s)" + +#: qt/manageprofiles/tab_remove_retention.py:344 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"As semanas começam a ser contadas a partir da semana atual. A semana começa " +"na Segunda-feira." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Mantenha uma cópia por semana pela(s) última(s)" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "semana(s)." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." msgstr "" +"Os meses são contados pelo calendário atual e iniciando a partir do mês " +"atual." -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Mantenha uma cópia por mês pelo(s) último(s)" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "mês/meses." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Os anos são contados pelo calendário e a partir do ano atual." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Mantenha todas cópias a cada ano por" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "todo os anos." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "... o espaço livre é menor que" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "... os inodes livres são menores que" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Remover as cópias de segurança antigas se …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "Autenticação é necessária para executar Back In Time como root." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" +"Autenticação é necessária para mudar cópias programadas acionadas por " +"conexões de dispositivos de armazenamento externos." -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Atalhos" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Lugares" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Sistema de Ficheiros" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Diretórios de Cópia de Segurança" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Perfil: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Perfil: {profile_name} (por utilizador \"{desktop_user}\")" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Ver Último Registro" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Iniciar {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Trabalhando…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "página do man: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importar configuração" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Nenhuma diretória selecionada" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importar" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Procurando…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Selecione a pasta de cópias de segurança a partir da qual o arquivo de " +"configuração deverá ser importado. O caminho será parecido com: {samplePath}" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Se a pasta estiver localizada numa unidade externa ou remota, terá de ser " +"montada manualmente antes." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Procurar novamente" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Mostrar diretórios ocultos" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Mostrar/esconder diretórios escondidos (Ctrl+H)" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Nenhuma configuração encontrada neste diretório" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Busca completa." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Mostrar registro completo" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Contagem até Encerramento" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "A cópia de segurança acabou." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Cancelar Encerramento" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Encerrar Agora" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "O sistema irá encerrar em {n} segundo." +msgstr[1] "O sistema irá encerrar em {n} segundos." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Opções sobre a comparação de cópias" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Comando:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parâmetros:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "" +msgstr "Utilize %1 e %2 para os parâmetros do caminho" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Por favor, defina um comando diff ou pressione Cancelar." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"O comando \"{cmd}\" não pode ser encontrado neste sistema. Por favor, tente " +"algo diferente ou aperte Cancelar." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Nenhum parâmetro definido para o comando diff. Utilizando o valor padrão " +"\"{params}\"." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Cópias de Segurança" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Apenas cópias diferentes" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Enumerar apenas cópias que sejam iguais a:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Verificação profunda (mais precisa, mas lenta)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Eliminar" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Selecionar Todos" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Comparar" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Ir para" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opções" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Não é possível comparar uma cópia de segurança a si mesma, já que a " +"comparação seria redundante." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" msgstr "" +"Deseja realmente apagar {file_or_dir} da cópia de segurança {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Deseja realmente apagar {file_or_dir} das {count} cópias?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "AVISO: Isto não pode ser revogado." -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Comando não encontrado: %s" +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Excluir {path} das futuras cópias?" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Modo root" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" +"Back In Time está a correr sem privilégios de root (completo acesso ao " +"sistema)" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Hoje" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Ontem" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Esta semana" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Semana passada" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Última marcação {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Isto NÃO É uma cópia de segurança, mas uma visualização em tempo real dos " +#~ "seus arquivos locais." diff --git a/common/po/pt_BR.po b/common/po/pt_BR.po index 4531de0af..0149a9778 100644 --- a/common/po/pt_BR.po +++ b/common/po/pt_BR.po @@ -1,1218 +1,1540 @@ -# Brazilian Portuguese translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Andre Desgualdo Pereira +# SPDX-FileCopyrightText: © Scythemare +# SPDX-FileCopyrightText: © Yuri Musachio # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2017-01-23 04:37+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Brazilian Portuguese \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-06-09 11:28+0000\n" +"Last-Translator: pablolucas890 \n" +"Language-Team: Portuguese (Brazil) \n" +"Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Falha ao salvar o arquivo de configuração: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Falha ao carregar o arquivo de configuração: %s" +"X-Generator: Weblate 5.11.4\n" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "O perfil \"%s\" já existe!" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Você não pode remover o último perfil!" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Desabilitado" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" +"Todas as licenças usadas neste projeto estão localizadas no diretório " +"{dir_link}. Para extrair informações de licença e direitos autorais por " +"arquivo usando metadados SPDX, consulte {readme_link}." -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Em cada início/reinício" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Aviso" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "A cada 5 minutos" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Perfil principal" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "A cada 10 minutos" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Local (criptografado com EncFS)" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "A cada 30 minutos" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (criptografado com EncFS)" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "A cada hora" +#: common/config.py:237 +msgid "Local" +msgstr "Local" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "A cada 2 horas" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Criptografia local" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "A cada 4 horas" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Criptografia" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "A cada 6 horas" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "A cada 12 horas" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Chave privada SSH" -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Horas customizadas" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Perfil: \"{name}\"" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "A cada Dia" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Diretório de backup inválido." -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Repetidamente (anacron)" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Pelo menos um diretório precisa ser selecionado para o backup." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Quando o drive for conectado (udev)" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Diretório: {path}" -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "A cada semana" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "" +"Este diretório não pode ser incluído no backup pois faz parte do próprio " +"destino do backup." -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "A cada mês" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Dia(s)" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Semana(s)" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Agendamento udev não funciona com o modo {mode}" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Ano(s)" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Falha ao escrever um novo crontab." -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Hora(s)" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron não está em execução, embora o comando crontab esteja disponível. Os " +"backups agendados não serão executados. O cron pode estar instalado, mas não" +" habilitado. Tente os dois comandos \"systemctl enable cron\" e \"systemctl " +"start cron\", ou consulte os canais de suporte da distribuição GNU/Linux " +"usada atualmente para assistência." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Falha ao salvar a configuração" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Falha ao carregar a configuração" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "O perfil \"{name}\" já existe." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "O último perfil não pode ser removido." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Não foi possível montar '{command}'" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Configuração para o diretório criptografado não encontrada." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Criar um novo diretório criptografado?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Cancelar" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Mês(es)" +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "Por favor, digite a senha para \"{user}\"." -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " EXPERIMENTAL!" +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "A senha não corresponde." -#: ../../common/config.py:129 -msgid "Local" -msgstr "Local" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Criar snapshot" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Não foi possível montar '{command}'" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Chave privada SSH" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Incapaz de desmontar {mountprocess} de {mountpoint}." -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Local encriptado" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"{command} não encontrado. Por favor, instale-o (ex. via \"{installcommand}\"" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Criptografia" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Ponto de montagem {mntpoint} não está vazio." -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH criptografado" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Digite a senha para o perfil {mode} \"{profile}\":" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Padrão" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Não foi possível instalar a regra Udev para o perfil {profile_id}. O serviço" +" DBus '{dbus_interface}' não estava disponível." -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Não foi possível encontrar o UUID para {path}" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "FALHOU" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Restaurar permissões" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Concluído" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Backup adiado devido ao uso de bateria" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Não foi possível encontrar o diretório de backups." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "Se estiver em uma unidade removível, por favor, insira-o." -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Aguardando {n} segundo." +msgstr[1] "Aguardando {n} segundos." -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Falha ao criar o backup {snapshot_id}." -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Por favor, aguarde. Finalizando…" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Não é possível criar a pasta." -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Perfil principal" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Salvando o arquivo de configuração…" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Perfil: \"%s\"" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Salvando as permissões…" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Pasta para Snapshots não é válida !" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "" +"Foi encontrado o backup incompleto {snapshot_id} que pode ser continuado." -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Você deve selecionar pelo menos uma pasta para backup !" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Removendo o diretório {snapshot_id} incompleto da última execução" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Você não pode incluir uma pasta de backup !" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Não é possível remover o diretório" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Você não pode incluir uma sub-pasta de backup !" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Criando backup" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s não é uma pasta !" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Sucesso" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Host/Usuário/Perfil não podem estar vazios!" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Transferência parcial devido a um erro" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" -"Não é possível escrever para: %s\n" -"Tem certeza de que tem acesso para gravação ?" +"Transferência parcial devido a arquivos de origem ausentes (consulte 'man " +"rsync')" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" -"O sistema de arquivos de destino em '%(path)s' está formatado com FAT que " -"não suporta hard-links. Por favor, utilize um sistema de arquivos nativo do " -"Linux." +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "O 'rsync' terminou com o código de saída {exit_code}" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Veja 'man rsync' para mais detalhes" -#: ../../common/config.py:407 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -"O sistema de arquivos de destino em '%(path)s' é um compartilhamento SMB " -"montado. Por favor, tenha certeza que o servidor SMB remoto suporta symlinks " -"ou ative a opção '%(copyLinks)s' em '%(expertOptions)s'." +"Códigos de saída negativos do rsync são números de sinal, veja 'kill -l' e " +"'man kill'" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Copiar links (criar links simbólicos)" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Nada mudou, nenhum novo backup é necessário" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Opções avançadas" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Não foi possível renomear {new_path} para {path}." -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" -"O sistema de arquivos de destino em '%(path)s' é um compartilhamento sshfs " -"montado. O sshfs não suporta hard-links. Por favor, utilize o modo 'SSH' " -"como alternativa." +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "Aplicar regras para remover os backups antigos" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Não é possível localizar crontab.\n" -"Você tem certeza se o cron está instalado ?\n" -"Se não você deve desabilitar todos os backups automáticos." +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "Aplicar política de retenção" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "Falha ao escrever um novo crontab." +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Tentando manter o mínimo de espaço livre" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"Não foi possível instalar a regra Udev para o perfil %(profile_id)s. O " -"serviço DBus '%(dbus_interface)s' não está disponível" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Tentando manter no mínimo {perc} de inodes livres" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Agendamento udev não funcionou com modo %s" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Agora" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Não foi possível encontrar UUID para \"%s\"" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Não foi possível montar {sshfs}" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." msgstr "" -"Não pude montar '%(command)s':\n" -"\n" -"%(error)s" - -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "Configuração para pasta encriptada não foi encontrada." +"ssh-agent não encontrado. Por favor, certifique-se de que está instalado." -#: ../../common/encfstools.py:136 +#: common/sshtools.py:489 msgid "" -"\n" -"Create a new encrypted folder?" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" -"\n" -"Criar uma nova pasta encriptada?" +"Não é possível desbloquear a chave privada ssh. A senha está errada ou não " +"está disponível para o cron." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Cancelar" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "O caminho remoto existe, mas não é um diretório." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Por favor, confirme o password" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "O caminho remoto não é gravável." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Password não corresponde" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "O caminho remoto não é executável." -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" -"As versões até 1.7.2 do encfs possuem um bug com a opção --reverse. Por " -"favor, atualize o encfs." +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Não é possível criar o caminho remoto." -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Tirar snapshot" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "O host remoto {host} não suporta {command}" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" -"Colisão de hash ocorreu em hash_id %s. Incrementando o valor global " -"hash_collision e tentando de novo." +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Verificação dos comandos no host {host} retornou um erro desconhecido" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "O host remoto {host} não suporta hardlinks" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Copiar chave ssh pública \"{pubkey}\" para o host remoto \"{host}\"." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Por favor, digite a senha para \"{user}\"." -#: ../../common/mount.py:590 -#, python-format +#: common/tools.py:382 +#, python-brace-format msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" -"%(user)s não é membro do grupo 'fuse'.\n" -" Execute 'sudo adduser %(user)s fuse'. Para aplicar as alterações, execute " -"um logout e logue novamente.\n" -"Veja o manual em 'man backintime' para instruções adicionais." +"O sistema de arquivos de destino para '{path}' está formatado com NTFS, que " +"possui incompatibilidades conhecidas com sistemas de arquivos estilo Unix." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "O ponto de montagem %s não está vazio." +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} não é um diretório válido." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "A criação do seguinte diretório falhou:" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Perfil '%(profile)s': Digite o password para %(mode)s: " +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "O acesso de escrita pode estar restrito." -#: ../../common/snapshotlog.py:62 +#: common/tools.py:471 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"O sistema de arquivos de destino para '{path}' está formatado com FAT, que " +"não suporta hard-links. Por favor, utilize um sistema de arquivos nativo do " +"GNU/Linux." -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "FALHOU" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Permissões de restauração:" - -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Pronto" - -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "Adiando o backup enquanto estiver na bateria" - -#: ../../common/snapshots.py:669 +#: common/tools.py:482 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" -"Não é possível localizar pasta de instantâneos.\n" -"Se ele estiver em uma unidade removível, por favor conecte-a." +"O sistema de arquivos de destino para '{path}' é um compartilhamento montado" +" via SMB. Por favor, tenha certeza que o servidor SMB remoto suporta links " +"simbólicos ou ative a opção '{copyLinks}' em '{expertOptions}'." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Esperando %s segundo." -msgstr[1] "Esperando % segundos." +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Copiar links (desreferenciar links simbólicos)" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Falha ao fazer snapshot %s !!!" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Opções avançadas" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Finalizando" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." +msgstr "" +"O sistema de arquivos de destino para '{path}' é um compartilhamento via " +"sshfs. O sshfs não suporta hard-links. Por favor, utilize o modo \"SSH\" em " +"vez disso." -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Não é possível criar a pasta: %s" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "A criação de arquivo falhou neste diretório:" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Salvar arquivo de configuração ..." +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Sobre o Back In Time" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Salvar permissão ..." +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Copyright:" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Autores:" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Tradutores:" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" +"Andre Desgualdo Pereira\n" +"Scythemare\n" +"Yuri Musachio " -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Não é possível remover pasta: %s" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Os créditos dos tradutores não disponíveis para a linguagem atual." -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Tirar snapshot" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "este link" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" +"Clique {thislink} para ver os créditos de tradutores para todos os idiomas." -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Não foi possível renomear %(new_path)s para %(path)s" - -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Remoção Inteligente" - -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Remover snapshots antigos" - -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "Tentar manter o mínimo espaço livre" - -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "Tente manter no mínimo %d%% de inodes livres" - -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "COM ERROS !" - -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Agora" - -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Não foi possível montar %s" - -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" -"Não foi possível destravar a chave privada ssh. O password está errado ou " -"não está disponível para o cron." +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Site do projeto" -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" -"A autenticação sem password para %(user)s@%(host)s falhou. Veja 'man " -"backintime' para instruções adicionais." +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Manual do usuário" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +#: qt/aboutdlg.py:247 qt/app.py:546 +#, fuzzy +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Abrir o manual do usuário no navegador (local, se disponível, caso contrário" +" online)" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s não encontrado em ssh_known_hosts." - -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" -"O caminho remoto existe mas não é um diretório:\n" -" %s" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Versão{BOLDEND}: {version}" -#: ../../common/sshtools.py:470 -#, python-format +#: qt/app.py:332 +#, fuzzy, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" -"O caminho remoto não é gravável:\n" -" %s" +"{app_name} parece estar em execução pela primeira vez, pois nenhuma " +"configuração foi encontrada." -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:337 +#, fuzzy msgid "" -"Remote path is not executable:\n" -" %s" +"Import an existing configuration from a backup location or another computer?" msgstr "" -"O caminho remoto não é executável:\n" -" %s" +"Importar uma configuração existente (de um diretório de destino de backup ou" +" de outro computador)?" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: qt/app.py:379 +msgid "Then press OK." msgstr "" -"Não foi possível criar o caminho remoto:\n" -" %s" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "Ping %s falhou. O host está inativo ou o endereço está errado." +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Criar um backup" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" -"O host remoto %(host)s não suporta '%(command)s':\n" -"%(err)s\n" -"Veja 'man backintime' para instruções adicionais." +"Usar horário de modificação e tamanho para detectar alterações nos arquivos." -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" -"Os comandos de verificação no host %(host)s retornaram um erro " -"desconhecido:\n" -"%(err)s\n" -"Veja 'man backintime' para instruções adicionais." +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "Criar snapshot (modo checksum)" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "O host remoto %s não suporta hardlinks" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Usar checksums para detectar alterações nos arquivos." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +#, fuzzy +msgid "Pause backup process" +msgstr "Pausar o processo de snapshot" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +#, fuzzy +msgid "Resume backup process" +msgstr "Retomar o processo de snapshot" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Usar checksum para detectar mudanças" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +#, fuzzy +msgid "Stop backup process" +msgstr "Parar processo de snapshot" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:504 +#, fuzzy +msgid "Refresh backup list" +msgstr "Atualizar lista de snapshots" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Nomear o backup" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:512 +#, fuzzy +msgid "Remove backup" +msgstr "Remover snapshot" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Atualizar lista de snapshots" +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "Visualizar último log" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Nome do Snapshot" +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "Pelo menos um diretório precisa ser selecionado para o backup." -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Remover snapshot" +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" +msgstr "Visualizar último log" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Ver log dos snapshots" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Ver último log" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Gerenciar perfis…" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Configurações" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Editar user-callback" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" msgstr "Desligar" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "Desligar o sistema após a finalização do snapshot." +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." +msgstr "Desligar o sistema após o snapshot ser finalizado." + +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Configurar idioma…" -#: ../../qt/app.py:149 +#: qt/app.py:540 msgid "Exit" msgstr "Sair" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Ajuda" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "Página do manual: Back In Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Ajuda sobre o Arquivo de Configuração" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Exibe a página do manual sobre o Back In Time (backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Website" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "Página do manual: Arquivo de configuração de perfis" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Exibe a página do manual sobre o arquivo de configuração de perfis " +"(backintime-config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Abrir o site do Back In Time no navegador" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Registro de alterações" -#: ../../qt/app.py:167 +#: qt/app.py:570 +#, fuzzy +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" +"Abrir o manual do usuário no navegador (local, se disponível, caso contrário" +" online)" + +#: qt/app.py:573 msgid "FAQ" -msgstr "FAQ (perguntas mais freqüentes)" +msgstr "FAQ (Perguntas Frequentes)" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Abrir Perguntas Frequentes (FAQ) no navegador.\"" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Fazer uma pergunta" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" -msgstr "Relatar um problema (bug)" +msgstr "Reportar um bug" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "Sobre" +#: qt/app.py:584 +msgid "Translation" +msgstr "Traduções" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Para cima" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Exibe novamente a mensagem sobre participação na tradução." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Mostrar arquivos ocultos" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Transição de Criptografia (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Exibe novamente a mensagem sobre a remoção do EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "Sobre" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Restaurar" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." msgstr "" +"Restaurar os arquivos ou diretórios selecionados para o destino original." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Restaurar para ..." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Restaurar para …" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." msgstr "" +"Restaurar os arquivos ou diretórios selecionados para um novo destino." -#: ../../qt/app.py:234 +#: qt/app.py:615 +#, fuzzy msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Restaurar o diretório exibido atualmente e todos os seus conteúdos para o " +"destino original." -#: ../../qt/app.py:239 +#: qt/app.py:621 +#, fuzzy msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" +"Restaurar o diretório exibido atualmente e todos os seus conteúdos para um " +"novo destino." -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" -"Restaurar o arquivo ou pasta selecionado.\n" -"Se este botão estiver cinza, é provável que \"%(now)s\" está selecionado na " -"lista de snapshots à esquerda." +#: qt/app.py:624 +msgid "Up" +msgstr "Acima" + +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Exibir arquivos ocultos" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Snapshots" +#: qt/app.py:630 +#, fuzzy +msgid "Compare backups…" +msgstr "Comparar snapshots…" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Snapshot" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Versão Candidata a Lançamento" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Exibir" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Exibe a mensagem sobre esta versão candidata a lançamento novamente." -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Atalhos" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Backup" + +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Restaurar" + +#: qt/app.py:723 +msgid "&Help" +msgstr "&Ajuda" + +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -"Essa pasta não existe\n" -"no snapshot selecionado atualmente!" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "Adicionar para a Lista de Inclusões" +#: qt/app.py:780 +msgid "Automatic" +msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "Adicionar para a Lista de Exclusões" +#: qt/app.py:784 +msgid "Light icon" +msgstr "" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -"%(appName)s não está configurado. Você gostaria de restaurar uma " -"configuração anterior?" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Apenas ícones" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Apenas texto" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Texto abaixo dos ícones" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Texto ao lado do ícone" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." +msgstr "" +"Esse diretório não existe\n" +"no backup atual selecionado." + +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Não é possível localizar pasta de snapshots.\n" -"Se ele estiver em uma unidade removível, por favor conecte-a e pressione OK" +"Esse diretório não existe\n" +"no backup atual selecionado." -#: ../../qt/app.py:527 +#: qt/app.py:995 +#, fuzzy msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" -"Se você fechar essa janela, o Back In Time não será capaz de desligar o seu " -"sistema quando o snapshot estiver terminado.\n" -"Você realmente deseja fechar?" +"Se você fechar esta janela, o Back In Time não será capaz de desligar seu " +"sistema quando o snapshot for finalizado." + +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Fechar a janela mesmo assim?" -#: ../../qt/app.py:667 ../../qt/app.py:719 +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Concluído, nenhum backup necessário" + +#: qt/app.py:1260 msgid "Working:" msgstr "Trabalhando:" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Concluído, backup não necessário" +#: qt/app.py:1267 +msgid "Working" +msgstr "Trabalhando" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Erro:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Erro" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" msgstr "Enviado:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "Velocidade:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" msgstr "ETA:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Global" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Root" +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "Backups:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Home" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Restaurar {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Pastas de backup" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Restaurar {path} para …" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Tem certeza de que deseja remover o snapshot:\n" -"%s" - -#: ../../qt/app.py:1025 -#, python-format +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Olá\n" +"Você tem usado o Back In Time no idioma {language} algumas vezes até agora.\n" +"A tradução da sua versão instalada do Back In Time para o {language} está {perc} completa. Independentemente do seu nível de conhecimento técnico, você pode contribuir para a tradução e, para o Back In Time em si.\n" +"Por favor, visite a {translation_platform_url} se desejar contribuir. Para assistência adicional e dúvidas, por favor acesse o projeto do {back_in_time_project_website}.\n" +"Pedimos desculpas pela interrupção, e esta mensagem não será exibida novamente. Este diálogo está disponível a qualquer momento pelo menu de ajuda.\n" +"Sua equipe Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "plataforma de tradução" + +#: qt/app.py:1658 +msgid "Website" +msgstr "através do site" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Sua tradução" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "No Fediverse do Mastodon: {link_and_label}." + +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Lista de emails {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Lista de emails {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} no site do projeto." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Abra uma demanda" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Alternativamente, você pode usar outro canal de sua escolha." + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Esta versão do Back In Time é um Candidato de Lançamento e é destinada principalmente para testes de estabilidade em preparação para o próximo lançamento oficial.\n" +"Nenhum dado do usuário ou telemetria é coletado. No entanto, a equipe do Back In Time está muito interessada em saber se o Candidato de Lançamento está sendo utilizado e se vale a pena continuar a fornecer essas versões de pré-lançamento.\n" +"Portanto, a equipe solicita gentilmente um breve feedback sobre se você testou esta versão, mesmo que não tenha encontrado nenhum problema. Mesmo um teste rápido de alguns minutos nos ajudaria muito.\n" +"As seguintes opções de contato estão disponíveis:\n" +"{contact_list}\n" +"Nesta versão, esta mensagem não será exibida novamente, mas pode ser acessada a qualquer momento pelo menu de ajuda.\n" +"Obrigado pelo seu apoio e por nos ajudar a melhorar o Back In Time!\n" +"Sua equipe do Back In Time" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/app.py:1841 +#, fuzzy +msgid "Proceed with the backup?" +msgstr "Remover este backup?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Todos os arquivos mais recentes em {path} serão removidos. Continuar?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -"Novas versões dos arquivos serão renomeadas com o sufixo '%(suffix)s' antes " -"da restauração.\n" -"Se você não precisar mais delas, você pode removê-las com o comando '%(cmd)s'" +"Todos os arquivos mais recentes no diretório original serão removidos. " +"Continuar?" -#: ../../qt/app.py:1038 +#: qt/app.py:1875 +#, fuzzy, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}Atenção{BOLDEND}: Deletar arquivos no sistema de arquivos raiz pode " +"quebrar todo o seu sistema." + +#: qt/app.py:2107 +#, fuzzy +msgid "Backup name" +msgstr "&Backup" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Remover este backup?" +msgstr[1] "Remover estes backups?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" +"As configurações de idioma só têm efeito após reiniciar o Back in Time." -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" msgstr "" -#: ../../qt/app.py:1083 -#, python-format +#: qt/backintime-qt-root.desktop:23 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "Você tem certeza que deseja restaurar esse(s) arquivo(s):" +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "Manter os snapshots nomeados." -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Pergunta" + +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" -"Você tem certeza que deseja remover todos os arquivos mais novos na sua " -"pasta original?" +"Criar cópias de backup com o sufixo {suffix}\n" +"antes de sobrescrever ou remover elementos locais." -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, fuzzy, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" -"AVISO: deletar arquivos no sistema de arquivos raiz (root) poderá destruir " -"todo o seu sistema!!!" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Visualizar o conteúdo atual do disco" +"Versões mais recentes dos arquivos serão renomeadas com o sufixo {suffix} " +"antes da restauração. Se não precisar mais deles, poderá removê-los com o " +"seguinte comando:" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Snapshot: %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Ver o snapshot feito em %s" +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Restaure apenas elementos que não existem ou\n" +"que são mais recentes do que os do destino.\n" +"Usando a opção \"rsync --update\"." -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Restaurar '%s'" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Remover os elementos mais recentes do diretório original." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Restaurar '%s' para ..." +#: qt/confirmrestoredialog.py:135 +#, fuzzy +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Restaure os arquivos ou diretórios selecionados para o destino original e " +"exclua os arquivos ou diretórios que não estão no snapshot. Tenha extrema " +"cautela, pois isso excluirá arquivos e diretórios que foram omitidos durante" +" a criação do snapshot." + +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Você realmente deseja restaurar este elemento no novo diretório?" +msgstr[1] "Você realmente deseja restaurar estes elementos no novo diretório?" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Você realmente deseja restaurar este elemento?" +msgstr[1] "Você realmente deseja restaurar estes elementos?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Autores" +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Incluir arquivos e diretórios" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Traduções" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Adicione à Lista de Inclusão" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Licença" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Adicione à Lista de Exclusão" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Configurar idioma" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Traduzido: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Padrão do sistema" + +#: qt/languagedialog.py:142 +#, fuzzy +msgid "Use operating system's language." +msgstr "Use o idioma do sistema operacional." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "Visualização do Último Log" -#: ../../qt/logviewdialog.py:57 +#: qt/logviewdialog.py:63 msgid "Last Log View" msgstr "Visualização do Último Log" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "Visualização do Log do Snapshot" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Perfil:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Backups:" + +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Filtrar:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Erro, [I] Informação, [C] Alteração" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "decodificar caminhos" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" -msgstr "Todos" +msgstr "Tudo" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Alterações" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" msgstr "Erros" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "Alterações" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informação" +msgstr[1] "Informações" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Informações" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "Falhas na transferência do rsync (experimental)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Erro, [I] Informação, [C] Mudar" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Gerenciar perfis" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "decodificar caminhos" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Editar" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Adicionar" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Remover" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Geral" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Erro" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Incluir" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "Pergunta" - -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "Inicializar BackInTime" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Excluir" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Trabalhando..." +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Remover & Reter" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Hoje" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Opções" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Ontem" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "O&pções Avançadas" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Esta semana" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Restaurar Configuração" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Semana passada" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Novo perfil" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "Isto NÃO é um snapshot, mas uma visualização do seus arquivos locais" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Renomear perfil" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "Última verificação %s" +#: qt/manageprofiles/__init__.py:215 +#, fuzzy, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Você tem certeza que deseja excluir o perfil \"{name}\" ?" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "Mostrar Log Completo" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Editar" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." +msgstr "" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "Modificar para Backup do Sistema Completo" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "Adicionar" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "Excluir" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Geral" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Modo:" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -"Aviso: %(app)s utiliza EncFS para criptografia. Uma auditoria de " -"segurança recente revelou diversos vetores de ataque possíveis para o EncFS. " -"Por favor, olhe a seção 'A NOTE ON SECURITY' em 'man backintime'." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Onde salvar os snapshots" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "Pasta" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "Configurações do SSH" +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "Pausar o processo de snapshot" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Host:" +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "Excluir arquivo" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Porta:" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Usuário:" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Caminho:" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Chave privada:" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" msgstr "" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Password" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Salvar o Password no Chaveiro" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" msgstr "" -"Armazenar o Password para o Cron (Problema de segurança: o root poderá ler o " -"password)" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Avançado" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Diretórios de backup" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "Não é possível remover o diretório" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Exibir arquivos ocultos" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "Caminho completo do snapshot: " +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Parar processo de snapshot" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Excluir diretório" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "padrão" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Agendar" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" msgstr "Dia:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "Dia da Semana" +msgstr "Dia da Semana:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Hora:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Tempo:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" msgstr "Horas:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "depois da hora" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minutos:" + +#: qt/manageprofiles/schedulewidget.py:93 +#, fuzzy +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Executar o Back In Time assim que o dispositivo for conectado (apenas uma " +"vez a cada X dias). Você será solicitado a fornecer sua senha sudo." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." @@ -1220,531 +1542,1121 @@ msgstr "" "Executar o Back In Time repetidamente. Isto é útil se o computador não é " "executado regularmente." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" msgstr "Todo:" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Habilitar registro de mensagens de depuração" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "Grava mensagens de nível de depuração no log do sistema via \"--debug\"." + +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -"Executar o Back In Time assim que o dispositivo for conectado (apenas uma " -"vez a cada X dias).\n" -"O seu password para o sudo será solicitado." +"Cuidado: Use-o apenas temporariamente para diagnóstico por gerar um excesso " +"de resultados." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Incluir" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Desabilitado" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Incluir arquivos e pastas" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Em cada início/reinício" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Adicionar arquivo" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "A cada {n} minuto" +msgstr[1] "A cada {n} minutos" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "A cada hora" +msgstr[1] "A cada {n} horas" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Adicionar pasta" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Horas personalizadas" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Excluir" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Todo dia" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" -"Aviso: Os wildcards ('foo*', '[fF]oo', 'fo?') serão ignorados no modo " -"'SSH criptografado'.\n" -"Apenas asteriscos separados são permitidos ('foo/*', 'foo/**/bar')" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Repetidamente (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Quando o drive for conectado (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Toda semana" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Todo mês" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Excluir padrões, arquivos e pastas" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Todo ano" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Altamente recomendado:" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Hora(s)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Dia(s)" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "Adicionar Padrões" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Semana(s)" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "Excluir arquivos maiores que: " +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Mês(es)" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" -"Excluir arquivos maiores que o valor em %(prefix)s.\n" -"Se o 'Modo de resincronismo total' estiver desabilitado, essa opção irá " -"afetar apenas novos\n" -"arquivos, porque, para o rsync, isso é uma opção de transferência e não de " -"exclusão.\n" -"Dessa forma, arquivos grandes que foram copiados anteriormente permanecerão " -"nos\n" -"snapshots mesmo que tenham sido alterados." +"Horas personalizadas podem ser apenas uma lista de horários separados por " +"vírgula (ex. 8,12,18,23) ou */3 para backups periódicos a cada 3 horas." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Remoção-automatica" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Mai vechi de:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Alegeți un fișier de cheie privată existent, din altă parte." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Dacă spațiul disponibil este mai mic de:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Creează o nouă cheie SSH fără parolă." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Ruta completă: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Cheie privată:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Utilizează configurația SSH a sistemului" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Lasă fișierul cheie neselectat. Conexiunile SSH se vor baza pe configurația " +"clientului existentă în sistem (de exemplu, ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Proxy SSH" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Gazdă:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Utilizator:" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Conectați-vă la gazda țintă prin acest proxy (cunoscut și ca gazdă de salt)." +" Consultați „-J” în documentația comenzii «ssh» sau „ProxyJump” în pagina de" +" manual „ssh_config” pentru detalii." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" +"{BOLD}Informații{ENDBOLD}: În modul „SSH criptat”, doar asteriscurile simple" +" sau duble sunt funcționale (de exemplu {example2}). Alte tipuri de " +"caractere joker și modele vor fi ignorate (de exemplu {example1}). Numele " +"fișierelor sunt impredictibile în acest mod din cauza criptării cu EncFS." -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opțiuni" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Exclude modele, fișiere sau directoare" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Activează notificările" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Adaugă un model" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Adaugă fișiere" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Adaugă directoare" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Sugestii" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" +"Selectați din elementele utilizate frecvent pentru a le adăuga la lista de " +"excluderi." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Exclude fișierele mai mari decât:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Exclude fișierele mai mari decât valoarea în {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Cu «Modul rsync complet» dezactivat, această opțiune afectează numai " +"fișierele nou create, deoarece rsync o tratează ca opțiune de transfer, și " +"nu ca regulă de excludere. În consecință, fișierele mari care au fost deja " +"copiate vor rămâne în copiile de rezervă, chiar dacă sunt modificate." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Exclude modelul" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Introduceți un model de excludere:" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" +"Pentru ajutor, consultați secțiunea {link} din pagina de manual rsync." -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Deschide pagina de manual rsync" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Exclude fișiere" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Exclude directoare" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" +"Dezactivat pentru că acest model nu este funcțional în modul „SSH criptat”." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Aceste opțiuni sunt destinate configurațiilor avansate. Modificați-le numai " +"dacă sunteți pe deplin conștient de implicațiile lor." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Rulează «rsync» cu „{cmd}”:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "ca sarcină cron" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" - -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +msgstr "pe gazda de la distanță" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "atunci când se efectuează manual o copie de rezervă" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Instalați „nocache” pentru a activa această opțiune." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "pe mașina locală" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" +"Redirecționează ieșirea standard (stdout) către „/dev/null” în sarcinile " +"cron." -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron va trimite automat un email cu rezultatul sarcinilor cronjobs atașat " +"dacă un MTA este instalat." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" +"Redirecționează ieșirea de eroare standard (stderr) către „/dev/null” în " +"sarcinile cron." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron va trimite automat un email cu erorile sarcinilor cronjob atașate dacă " +"un MTA este instalat." + +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "Ko/sec" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Limitează utilizarea lățimii de bandă a «rsync»:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "" +msgstr "Păstrează ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "" +msgstr "Păstrează atributele extinse (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Restricționează la un singur sistem de fișiere" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Opțiunile trebuie să fie puse între ghilimele, de exemplu: {example}." -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Lipește opțiunile adiționale la «rsync»" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefix de rulat înainte de fiecare comandă pe gazda de la distanță." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Variabilele trebuie să fie eludate cu \\$FOO. Acest lucru nu afectează " +"«rsync». Deci, pentru a adăuga un prefix pentru «rsync» utilizați " +"„{example_value}” cu {rsync_options_value}." + +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "implicit" -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" -msgstr "" +msgstr "Adaugă un prefix la comenzile SSH" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Verifică dacă gazda de la distanță este conectată" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Avertisment: dacă este dezactivată și gazda de la distanță nu este " +"disponibilă, acest lucru ar putea duce la unele erori ciudate." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Verifică dacă gazda de la distanță acceptă toate comenzile necesare." + +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Avertisment: dacă este dezactivată și gazda de la distanță nu acceptă toate " +"comenzile necesare, acest lucru ar putea duce la unele erori ciudate." -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(implicit: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "dezactivată" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "activată" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Mod:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Unde să se salveze copiile de rezervă" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Configurări SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Ruta:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Fișierul de cheie:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Parola" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Salvează parola în inelul de chei" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Memorează parola pentru «cron» în cache (problemă de securitate: root poate " +"să citească parola)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avansat" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Ruta completă a copiei de rezervă:" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" +"Programarea este dezactivată deoarece nu a fost găsită nicio instalare " +"«cron». Vă rugăm să instalați «cron» pentru a activa copiile de rezervă " +"programate." + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Ruta de destinație a copiei de rezervă nu poate fi goală." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Parola de criptare nu poate fi goală." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"A apărut o eroare în timpul încercării de conectare la gazda de la distanță." +" A fost returnat următorul mesaj de eroare:" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Pentru a permite conectarea fără parolă, cheia SSH publică poate fi copiată " +"pe gazda de la distanță." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Continuați cu copierea cheii SSH?" + +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Cheia SSH publică nu a putut fi copiată. Acest lucru se poate datora unei " +"probleme de conexiune sau de permisiuni." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Autenticitatea gazdei {host} nu poate fi stabilită." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "Amprenta cheii {keytype} este:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Verificați această amprentă. Doriți să o adăugați la fișierul „known_hosts”?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Doriți cu adevărat să schimbați directorul copiilor de rezervă?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Destinația de copie de rezervă selectată nu este goală." + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Aceasta trebuie să fie goală pentru a utiliza criptarea." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Fișier nevalid: Nu este o cheie SSH privată" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"Fișierul selectat ({path}) este o cheie SSH publică. Vă rugăm să alegeți " +"fișierul de cheie privată corespunzător (fără sufixul „.pub”)." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Profil nou" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" +"Fișierul {path} există deja. Nu se poate crea o nouă cheie SSH cu acest " +"nume." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Redenumește profil" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Nu s-a putut crea o cheie SSH nouă în {path}." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Sigur doriți să ștergeți profilul „%s” ?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Include fișiere și directoare" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"„{path}” este o legătură simbolică. Ținta asociată nu va fi salvată până " +"când nu este inclusă și ea." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Doriți să includeți în schimb ținta legăturii simbolice?" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Include fișiere" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Include directoare" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Activează notificările" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "" +"Dezactivează efectuarea de copii de rezervă atunci când mașina funcționează " +"pe baterie" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Starea alimentării nu este disponibilă din sistem" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Rulează doar o copiere de rezervă la un moment dat" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Alte operații de copiere de rezervă vor fi blocate până la finalizarea " +"operației de copiere curente. Aceasta este o opțiune globală, ceea ce " +"înseamnă că va afecta toate profilele pentru acest utilizator. Cu toate " +"acestea, trebuie să fie activată de asemenea pentru toți ceilalți " +"utilizatori." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Efectuează o copie de rezervă a fișierelor înlocuite la restaurare" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Continuă în caz de erori (păstrează copii de rezervă incomplete)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Utilizează suma de control pentru a detecta modificările" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." msgstr "" +"Creează o nouă copie de rezervă, indiferent dacă au existat modificări sau " +"nu." -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Avertizează dacă spațiul liber pe disc scade sub" + +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Afișează un avertisment atunci când spațiul liber de pe discul de destinație" +" al copiei de rezervă este mai mic decât valoarea specificată." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" +"Dacă este activată politica «Elimină și păstrează» și copiile de rezervă " +"vechi sunt eliminate pe baza spațiului liber disponibil, această valoare nu " +"poate fi mai mică decăt valoarea stabilită în politică." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Nivel de jurnalizare:" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Nimic" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "manual de utilizare" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Următoarele reguli sunt procesate de sus în jos. Regulile ulterioare " +"prevalează asupra celor anterioare. Consultați {manual_link} pentru detalii " +"și exemple." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Deschide manualul de utilizare în navigator." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Exclude fișier" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Păstrează cea mai recentă copie de rezervă." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Exclude director" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Cea mai recentă copie de rezervă este păstrată în orice situație." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Acest comportament nu poate fi modificat." + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Păstrează copiile de siguranță numite." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Copiile de rezervă care au primit un nume, pe lângă marcajul de timp " +"obișnuit, vor fi păstrate în orice situație și nu vor fi eliminate." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Include director" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "An (Ani)" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Elimină copiile de rezervă mai vechi de" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Zile întregi. Ziua curentă este ignorată." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Săptămâni calendaristice cu luni ca primă zi. Săptămâna curentă este " +"ignorată." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Perioade de 12 luni. Luna curentă este ignorată." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Politica de păstrare" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Rulează în fundal pe gazda de la distanță." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"Politica de păstrare va fi executată direct pe mașina de la distanță, nu " +"local. Comenzile „bash”, «screen» și «flock» trebuie să fie instalate și " +"disponibile pe mașina de la distanță respectivă." -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." msgstr "" +"Dacă este activată, Back In Time va testa mai întâi mașina de la distanță." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Zilele sunt numărate începând de astăzi." -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Păstrează toate copiile de rezervă pentru ultima(ultimele)" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "zi(zile)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Păstrează ultima copie de rezervă pentru fiecare zi din ultimele" + +#: qt/manageprofiles/tab_remove_retention.py:344 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Săptămânile sunt numărate începând cu săptămâna curentă. O săptămână începe " +"luni." -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" msgstr "" +"Păstrează ultima copie de rezervă pentru fiecare săptămână din ultimele" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "săptămână(săptămâni)." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Lunile sunt numărate ca luni calendaristice începând cu luna curentă." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Păstrează ultima copie de rezervă pentru fiecare lună din ultimele" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "lună(luni)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Anii sunt numărați ca ani calendaristici începând cu anul curent." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Păstrează ultima copie de rezervă pentru fiecare an din" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "toți anii." -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… spațiul liber este mai mic decât" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… nodurile-i libere sunt mai puține decât" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Elimină cea mai veche copie de rezervă dacă …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "Autentificarea este necesară pentru a rula Back In Time ca root." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" +"Autentificarea este necesară pentru a modifica programările de copie de " +"rezervă declanșate de conectările dispozitivelor de stocare externe." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Scurtături" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Locuri" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Sistemul de fișiere" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Directoare copie de rezervă" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: {profile_name}" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: {profile_name}(al utilizatorului „{desktop_user}”)" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Vizualizează ultimul jurnal" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Pornește {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Se lucrează…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "pagina de manual: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importă configurația" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Nu a fost selectat niciun director" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importă" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Se caută…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Selectați directorul de copii de rezervă din care trebuie importat fișierul " +"de configurare. Ruta poate arăta astfel: {samplePath}" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Dacă directorul este localizat pe o unitate externă sau la distanță, aceasta" +" trebuie să fie montată manual în prealabil." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Scanează din nou" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Afișează directoarele ascunse" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Afișează/ascunde directoarele ascunse (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Nu s-a găsit niciun fișier de configurare în acest director" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Căutare finalizată." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Afișează jurnalul complet" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Numărătoarea inversă până la oprire" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Copia de rezervă a fost finalizată." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Anulează oprirea" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Oprește acum" -#: ../../qt/snapshotsdialog.py:60 +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Sistemul se va opri în {n} secundă." +msgstr[1] "Sistemul se va opri în {n} secunde." +msgstr[2] "Sistemul se va opri în {n} de secunde." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Opțiuni privind compararea copiilor de rezervă" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Comandă:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametri:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "" +msgstr "Utilizați %1 și %2 pentru parametrii rutei" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Stabiliți o comandă «diff» sau apăsați butonul «Anulează»." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." msgstr "" +"Comanda „{cmd}” nu poate fi găsită pe acest sistem. Încercați altceva sau " +"apăsați butonul «Anulează»." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Nu s-au stabilit parametri pentru comanda «diff». Se utilizează valoarea " +"implicită «{params}»." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Copii de rezervă" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Doar copiile de rexervă care diferă" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Listează doar copiile de siguranță care sunt egale cu:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "" +msgstr "Verificare profundă (mai precis, dar încet)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Șterge" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Selectează toate" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Compară" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" -msgstr "Mergi la" +msgstr "Navighează la" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Opțiuni" + +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Nu este posibil să se compare o copie de rezervă cu ea însăși, deoarece " +"comparația ar fi redundantă." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" msgstr "" +"Sigur doriți să ștergeți {file_or_dir} din copia de rezervă {backup_id}?" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Comandă negăsită: %s" +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Sigur doriți să ștergeți {file_or_dir} din {count} copii de rezervă?" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "AVERTISMENT: Această acțiune nu poate fi revocată." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Excludeți {path} din copiile de rezervă viitoare?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Modul root" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" +"Back In Time rulează în prezent cu privilegii root (acces complet la sistem)" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Astăzi" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Ieri" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Săptămâna aceasta" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Săptămâna trecută" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Ultima verificare: {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Aceasta NU este o copie de rezervă, ci o vizualizare „live” a fișierelor " +#~ "locale." diff --git a/common/po/ru.po b/common/po/ru.po index 6bc78b515..1e2ac88ef 100644 --- a/common/po/ru.po +++ b/common/po/ru.po @@ -1,1691 +1,2607 @@ -# Russian translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Vadim Peretokin # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-10-30 18:04+0000\n" -"Last-Translator: Makar \n" -"Language-Team: Russian \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-08 06:18+0000\n" +"Last-Translator: AndrewG \n" +"Language-Team: Russian \n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Просмотр содержимого текущего диска" - -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Добавить каталог" - -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Автоматическое удаление" - -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Исключить каталог" - -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Включить каталог" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Не удалось сохранить конфигурацию: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Не удалось загрузить конфигурацию: %s" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Профиль \"%s\" уже существует!" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Невозможно удалить последний профиль!" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Отключён" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "При каждой загрузке системы" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Каждые 5 минут" - -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Каждые 10 минут" - -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Каждые 30 минут" - -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Каждый час" - -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Каждые 2 часа" - -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Каждые 4 часа" - -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Каждые 6 часов" - -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Каждые 12 часов" - -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Другое" +"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.15.2\n" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Каждый день" - -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"Все лицензии, используемые в этом проекте, находятся в папке {dir_link}. " +"Чтобы извлечь информацию о лицензии и авторских правах для каждого файла с " +"помощью метаданных SPDX, обратитесь к {readme_link}." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Когда подключено устройство (udev)" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Каждую неделю" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Каждый месяц" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Дней (Дня)" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Недель" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Лет (Года)" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Предупреждение" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Час(ов)" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Основной профиль" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Месяцев" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Локально (шифрование EncFS)" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " ЭКСПЕРИМЕНТАЛЬНО!" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (шифрование EncFS)" -#: ../../common/config.py:129 +#: common/config.py:237 msgid "Local" msgstr "Локально" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" - -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "личный ключ SSH" - -#: ../../common/config.py:131 +#: common/config.py:240 msgid "Local encrypted" -msgstr "Локальное шифрование" +msgstr "Локально, зашифровано" -#: ../../common/config.py:131 ../../common/config.py:132 +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 msgid "Encryption" msgstr "Шифрование" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH зашифровано" - -#: ../../common/config.py:135 -msgid "Default" -msgstr "По умолчанию" - -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" - -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" - -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" - -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" - -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" - -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" - -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" - -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" - -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" - -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" - -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" - -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" - -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Основной профиль" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Профиль: \"%s\"" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Каталог резервных копий задан неверно!" - -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Вы должны выбрать хотя бы одну папку для резервного копирования!" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Вы не можете добавить папку для резервного копирования!" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Приватный ключ SSH" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Вы не можете включить в архив вложенные каталоги" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Профиль: \"{name}\"" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s не папка !" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Папка снимков задана неверно." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Сервер/Пользователь/Профиль-ID не должны быть пустыми!" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Для резервного копирования должна быть выбрана хотя бы одна папка." -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Не удаётся произвести запись в: %s\n" -"Вы уверены, что имеете право на запись?" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Папка: {path}" -#: ../../common/config.py:402 -#, python-format +#: common/config.py:332 common/config.py:347 msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" -"Файловая система на '%(path)s' отформатирована в FAT которая не поддерживает " -"\"жесткие\" ссылки. Используйте нативные файловые системы для Linux." +"Эта папка не может быть включен в резервную копию, поскольку она является " +"частью самого места назначения резервной копии." -#: ../../common/config.py:407 -#, python-format +#: common/config.py:366 +#, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." msgstr "" +"Значение для параметра \"Удалить самую старую резервную копию, если " +"свободного места меньше чем\" ({val_one}) должно быть меньше или равно " +"пороговому значению для параметра \"Предупреждать, если свободного места на " +"диске становится меньше чем\" ({val_two})." -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Копировать ссылки (разыменование символических ссылок)" - -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Расширенные настройки" - -#: ../../common/config.py:413 -#, python-format +#: common/config.py:371 msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." msgstr "" +"Пожалуйста, измените настройки таким образом, чтобы лимит удаления резервной" +" копии не превышал лимит предупреждения." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"Невозможно найти crontab\n" -"Вы уверены, что cron установлен?\n" -"Если не установлен, вы должны отключить автоматическое резервное копирование." +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Расписание udev не работает в режиме {mode}" -#: ../../common/config.py:1478 +#: common/config.py:1569 msgid "Failed to write new crontab." -msgstr "Не удалось создать новую задачу cron" - -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"Не удалось установить Udev правило для профиля %(profile_id)s. DBus сервис " -"'%(dbus_interface)s' недоступен" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "Расписание udev неработает в этом режиме %s" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "Не удалось найти UUID для \"%s\"" - -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" -"Не могу смонтировать '%(command)s':\n" -"\n" -"%(error)s" +msgstr "Не удалось записать новый crontab." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "Не найдена конфигурация для зашифрованной директории." - -#: ../../common/encfstools.py:136 +#: common/config.py:1577 msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" -"\n" -"Создать новую зашифрованную папку?" - -#: ../../common/encfstools.py:137 +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron не запущена, хотя присутствует команда crontab. Резервное копирование " +"по расписанию выполняться не будет. Возможно, cron установлена, но не " +"включена. Попробуйте выполнить команду «system enable cron» или свяжитесь с " +"поддержкой вашего дистрибутива Linux." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Не удалось сохранить конфигурацию" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Не удалось загрузить конфигурацию" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Профиль \"{name}\" уже существует." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Последний профиль не может быть удален." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Невозможно смонтировать \"{command}\"" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Не найдена конфигурация для зашифрованной папки." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Создать новую зашифрованную папку?" + +#: common/encfstools.py:204 msgid "Cancel" msgstr "Отмена" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Пожалуйста, подтвердите пароль" - -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Пароль не подходит" - -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." msgstr "" -"encfs версии 1.7.2 и ранние имеют баг с опцией --reverse. Пожалуйста, " -"обновите encfs." +"Для подтверждения повторите ввод пароля для зашифрованной файловой системы." -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Сделать резервную копию" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "Пароль для EncFS не подходит." -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" -"В hash_id %s возникла хеш-коллизия. Увеличьте глобальную переменую " -"hash_collision и попробуйте снова." +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Сделать снимок" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "Не удалось размонтировать %(proc)s из %(mountpoint)s" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Не удается инициализировать зашифрованный путь \"{command}\"" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "%(proc)s не надено. Пожалуйста установить нап. %(install_command)s" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Не удалось размонтировать {mountprocess} из {mountpoint}." -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" -"%(user)s не является членом группы 'fuse'.\n" -" Запустите 'sudo adduser %(user)s fuse'. Чтобы применить изменения, выйдите " -"и войдите снова.\n" -"Выполните 'man backintime' для дальнейших инструкций." - -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "точка монтирования %s не пустая." +"Программа {command} не найдена. Установите ее (например через " +"\"{installcommand}\")" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Точка монтирования {mntpoint} не пустая." -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "Профиль '%(profile)s': Введите пароль для %(mode)s: " +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Введите пароль для {mode} профиля \"{profile}\":" -#: ../../common/snapshotlog.py:62 +#: common/schedule.py:238 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." msgstr "" +"Не удалось установить правило Udev для профиля {profile_id}. Служба DBus " +"'{dbus_interface}' недоступна." + +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Не удалось найти UUID для {path}" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 +#: common/snapshots.py:378 common/snapshots.py:656 msgid "FAILED" msgstr "НЕУСПЕШНО" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "Восстановить права:" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Восстановить права" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 msgid "Done" msgstr "Готово" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" msgstr "" +"Следующие записи из включаемого списка не имеют соответствующих файла или " +"папки в источнике резервной копии:" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Отсрочка резервного копирования при работе от батареи" + +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Невозможно найти папку снимков." + +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." msgstr "" -"Невозможно найти каталог резервных копий!\n" -"Если он находится на съёмном диске, пожалуйста, подключите его." +"Если она находится на съёмном диске, пожалуйста, подключите его и нажмите " +"ОК." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "Ждать %s сек." -msgstr[1] "Ждать %s сек." -msgstr[2] "Ждать %s сек." +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Ожидание {n} секунду." +msgstr[1] "Ожидание {n} секунды." +msgstr[2] "Ожидание {n} секунд." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "Не удалось создать резервную копию %s !!!" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Не удалось создать резервную копию {snapshot_id}." -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Окончание" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Пожалуйста, подождите. Завершение…" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Не могу создать каталог: %s" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Невозможно создать папку." -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "Сохранить конфигурационный файл..." +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Сохранение файла конфигурации…" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "Сохранить разрешение ..." +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Сохранение разрешений…" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "Найден незавершённый бекап {snapshot_id}, который можно продолжить." -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" msgstr "" +"Удаление папки незавершённого {snapshot_id}, оставшегося после последнего " +"запуска" + +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Невозможно удалить папку" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Создание резервной копии" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Успешно" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Неполная передача из-за ошибки" + +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" +"Частичный перенос из-за исчезновения исходных файлов (см. «man rsync»)" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Не могу удалить каталог: %s" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "rsync завершился c кодом {exit_code}" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Сделать резервную копию" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Подробнее см. «man rsync»" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" +"Неудачные коды выхода из rsync - это номера сигналов, см. 'kill -l' и 'man " +"kill'" + +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Ничего не изменилось, нет необходимости в новой резервной копии" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "Не удалось переименовать %(new_path)s в %(path)s" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Невозможно переименовать {new_path} в {path}." -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Умное удаление" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Удаление старых резервных копий" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Удалить старые резервные копии" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Применяется политика хранения" -#: ../../common/snapshots.py:1427 +#: common/snapshots.py:2041 msgid "Trying to keep min free space" msgstr "Стараться сохранять минимальное свободное место" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" - -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "С ОШИБКАМИ!" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Попытка сохранить минимум {perc} свободных инодов" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 msgid "Now" msgstr "Сейчас" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "Не могу смонтировать %s" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Невозможно смонтировать {sshfs}" -#: ../../common/sshtools.py:315 +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-агент не найден. Убедитесь, что он установлен." + +#: common/sshtools.py:489 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" -"Не удалось разблокировать приватный ключ ssh. Неверный пароль или пароль не " +"Не удалось разблокировать приватный ключ ssh. Пароль неверный или не " "подходит для cron." -#: ../../common/sshtools.py:345 -#, python-format +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Удаленный путь существует, но не является папкой." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Удаленный путь недоступен для записи." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Удаленный путь не доступен для выполнения." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Не удалось создать удаленный путь." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Удаленный узел {host} не поддерживает{command}" + +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Команды проверки на хосте {host} вернули неизвестную ошибку" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Удаленный хост {host} не поддерживает жесткие ссылки" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Скопировать открытый ssh-ключ \"{pubkey}\" на удаленный хост \"{host}\"." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Пожалуйста, подтвердите пароль для \"{user}\"." + +#: common/tools.py:382 +#, python-brace-format msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"Файловая система назначения {path} отформатирована в NTFS и не полностью " +"совместима с файловыми системами Unix." -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} существует, но не является папкой." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Не удалось создать папку:" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "%s не найдено в ssh_known_hosts." +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Доступ на запись может быть ограничен." -#: ../../common/sshtools.py:468 -#, python-format +#: common/tools.py:471 +#, python-brace-format msgid "" -"Remote path exists but is not a directory:\n" -" %s" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" -"Удаленный путь доступен, но это не дериктория:\n" -"%s" +"Файловая система назначения {path} отформатирована в FAT и не поддерживает " +"\"жесткие\" ссылки. Используйте нативные файловые системы GNU/Linux." -#: ../../common/sshtools.py:470 -#, python-format +#: common/tools.py:482 +#, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" -"Удаленный путь не доступен для записи:\n" -"%s" +"Файловая система назначения {path} является сетевым ресурсом " +"примонтированным через SMB. Убедитесь, что удаленный SMB-сервер поддерживает" +" симлинки, или активируйте \"{copyLinks}\" в \"{expertOptions}\"." + +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Копировать ссылки (разыменование символических ссылок)" + +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Расширенные настройки" -#: ../../common/sshtools.py:472 -#, python-format +#: common/tools.py:491 +#, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"Файловая система назначения для {path} является сетевым ресурсом " +"смонтированным через sshfs. Sshfs не поддерживает \"жесткие\" ссылки. " +"Используйте режим «SSH»." -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Не удалось создать файл в папке:" + +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Инфо про Back In Time" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Авторские права:" + +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Разработчики:" + +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Переводы:" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Vadim Peretokin " + +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Для текущего языка титров переводчика нет." + +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "эту ссылку" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -"Не удалось создать удаленный путь:\n" -"%s" +"Для просмотра титров переводчиков для всех языков откройте {thislink}." + +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Сайт проекта" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Руководство пользователя" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -"Отсутсвует подключение к %s. Сервер не отвечает или неправильный адрес." +"Открыть руководство пользователя в браузере (локальное если есть, иначе в " +"интернете)" + +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Версия{BOLDEND}: {version}" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"Похоже, {app_name} запускается впервые, так как конфигурация не найдена." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/app.py:337 msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Импортировать существующую конфигурацию (из резервной целевой папки или с " +"другого компьютера)?" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Затем щёлкни ОК." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Создать резервную копию" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" +"Используйте время модификации и размер для обнаружения изменений файла." -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "Использовать контрольную сумму для обнаружения изменений" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Создать резервную копию (режим контрольной суммы)" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Использовать контрольную сумму для обнаружения изменений." -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Приостановить процесс создания резервной копии" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Продолжить процесс создания резервной копии" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "Обновить список резервных копий" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Остановить процесс создания резервной копии" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Обновить список моментальных резервных копий" + +#: qt/app.py:508 +msgid "Name backup" msgstr "Название резервной копии" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" +#: qt/app.py:512 +msgid "Remove backup" msgstr "Удалить резервную копию" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "Просмотр журнала снимков состояния" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Открыть журнал" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "Просмотреть последний журнал" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Просмотр журнала выбранной резервной копии." + +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Открыть журнал последней резервной копии" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Настройки" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "Просмотреть журнал последней резервной копии." -#: ../../qt/app.py:142 +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Управление профилями…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Редактирование отзыва пользователя" + +#: qt/app.py:532 msgid "Shutdown" msgstr "Выключить" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "Выключить систему после завершения задания." +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Выключить систему после завершения создания резервной копии." + +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Изменить язык…" -#: ../../qt/app.py:149 +#: qt/app.py:540 msgid "Exit" msgstr "Выйти" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Справка" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "Страница man: Back In Time" -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "Справка о файле конфигурации" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Показать страницу man по Back In Time (backintime)" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Сайт" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "Страница man для файла конфигурации профилей" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Показать страницу man про файл конфигурации профиля (backintime-config)" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Открыть в браузере сайт Back In Time" + +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" msgstr "Список изменений" -#: ../../qt/app.py:167 +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "Открыть список изменений (локально если можно, иначе из интернета)" + +#: qt/app.py:573 msgid "FAQ" -msgstr "Часто задаваемые вопросы" +msgstr "ЧаВо" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Открыть в браузере список часто задаваемых вопросов" + +#: qt/app.py:577 msgid "Ask a question" msgstr "Задать вопрос" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" msgstr "Сообщить об ошибке" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "О программе" +#: qt/app.py:584 +msgid "Translation" +msgstr "Перевод" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Вверх" +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Показать сообщение об участии в переводе." -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Показать скрытые файлы" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Миграция с шифрования EncFS" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Показать сообщение об удалении EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "О программе" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 msgid "Restore" msgstr "Восстановить" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "Восстановить выбранные файлы или папки в исходное место назначения." -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "Восстановить в ..." +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Восстановить в…" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "Восстановить выбранные файлы или папки в новое место назначения." -#: ../../qt/app.py:234 +#: qt/app.py:615 msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +"Restore the currently shown directory and all its contents to the original " +"location." msgstr "" +"Восстановление текущей показанной папки и всего её содержимого в исходное " +"место." -#: ../../qt/app.py:239 +#: qt/app.py:621 msgid "" -"Restore the currently shown folder and all its content to a new destination." +"Restore the currently shown directory and all its contents to a new " +"location." msgstr "" +"Восстановление текущей показанной папки и всего её содержимого в новое " +"место." -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "Вверх" + +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Показать скрытые файлы" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Резервные копии" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Сравнить резервные копии…" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "Резервная копия" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Кандидат на релиз" -#: ../../qt/app.py:271 -msgid "View" -msgstr "Вид" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Снова показывает сообщение об этом релизе-кандидате." -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Ярлыки" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Резервное копирование" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "Включить" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Восстановление" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "Добавить в исключения" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Справка" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Значок в системной планке" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "Автоматически" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Светлый" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Тёмный" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Только значки" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Только текст" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Текст под значками" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Текст рядом со значками" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" -"%(appName)s не настроена. Вы хотите восстановить предыдущую конфигурацию?" +"Этой папки не существует\n" +"в выбранной резервной копии." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Невозможно найти каталог резервных копий!\n" -"Если он находится на съёмном диске, пожалуйста, подключите его и нажмите ОК" +"Этой папки не существует\n" +"в выбранной резервной копии." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Если вы закроете это окно, то Back In Time не сможет выключить систему после" +" завершения создания резервной копии." -#: ../../qt/app.py:667 ../../qt/app.py:719 +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Закрыть окно в любом случае?" + +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Сделано, резервная копия не нужна" + +#: qt/app.py:1260 msgid "Working:" msgstr "Работаю:" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Всё сделано, сохранять ничего не надо" +#: qt/app.py:1267 +msgid "Working" +msgstr "Работаю" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Ошибка:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Ошибка" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" msgstr "Отправлено:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" msgstr "Скорость:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Общее" +msgstr "Ещё примерно:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Корневой каталог" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Резервное копирование:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Домашний каталог" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Восстановить {path}" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Резервные директории" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Восстановить {path} в…" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"Вы уверены что вы хотите удалить резервную копию:\n" -"%s" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Здравствуйте!\n" +"При работе с Back In Time вы используете {language} язык.\n" +"Перевод установленной вами версии Back In Time на {language} завершён на {perc}. Каким бы ни был ваш уровень знаний, вы можете внести свой вклад в перевод и тем самым помочь Back In Time.\n" +"Если вы хотите поучаствовать в переводе — пожалуйста, посетите {translation_platform_url}. Задать вопросы и получить помощь можно на {back_in_time_project_website}.\n" +"Приносим извинения за неудобства — это сообщение больше не появится. Снова найти это диалоговое окно можно будет в меню помощи.\n" +"Ваша команда Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "платформы для перевода" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Сайт" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Ваш перевод" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "В децентрализованной сети Mastodon: {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Email: {link_and_label}." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Список рассылки: {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} на сайте проекта." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Подать заявку" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Либо вы можете использовать любо другой способ связи." -#: ../../qt/app.py:1038 +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Эта версия Back In Time является версией-кандидатом на релиз и в основном предназначена для тестирования стабильности и подготовки к к следующему официальному выпуску.\n" +"Никаких пользовательских данных и телеметрии не собирается. Однако команде Back In Time очень интересно узнать, используется ли версия-кандидат и стоит ли продолжать предоставлять такие предварительные версии.\n" +"Поэтому команда любезно просит вас оставить краткий отзыв о том, тестировали ли вы эту версию, даже если у вас не возникло никаких проблем. Даже быстрый тестовый запуск в течение нескольких минут нам бы очень помог.\n" +"Доступны следующие варианты связи:\n" +"{contact_list}\n" +"В этой версии это сообщение больше не будет отображаться, но к нему можно получить доступ в любое время через меню \"Справка\".\n" +"Спасибо вам за вашу поддержку и за помощь в улучшении Back In Time!\n" +"Ваша команда Back In Time" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"В целевой папке доступно {free} свободного места, что ниже " +"сконфигурированного порога в {threshold}." + +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Продолжить резервное копирование?" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "Все новые файлы в {path} будут удалены. Продолжать?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" +"Все файлы, которые новее, из исходной папки будут удалены. Продолжить?" -#: ../../qt/app.py:1072 +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}ВНИМАНИЕ{BOLDEND}: Удаление файлов в корне файловой системы может " +"сломать всю вашу систему." + +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Название бекапа" -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Эту резервную копию удалить?" +msgstr[1] "Эти резервные копии удалить?" +msgstr[2] "Эти резервные копии удалить?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "Смена языка произойдет только после перезапуска Back In Time." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Версионные резервные копии (root)" + +#: qt/backintime-qt-root.desktop:23 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"GUI для версионных резервных копий, которые уменьшают расход диска (режим " +"root)" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "Вы действительно хотите восстановить этот файл(ы):" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Версионные резервные копии" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Графический интерфейс для версионных резервных копий, которые уменьшают " +"расход диска" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Вопрос" -#: ../../qt/app.py:1101 +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Создать резервные копии с последующим {suffix} перед перезаписью или " +"удалением локальных файлов." -#: ../../qt/app.py:1105 +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Более новые версии файлов перед восстановлением будут переименованы с " +"добавлением {suffix}. Эти файлы могут быть удалены следующей командой:" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "Резервная копия: %s" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "Посмотреть резервную копию, которая сделана %s" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Восстанавливать только несуществующие элементы или элементы новее, чем " +"имеющиеся. Использует опцию \"{rsync_example}\"." -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "Восстановить '%s'" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Удалять более новые файлы в исходной папке." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "Восстановить '%s' в ..." +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Восстановить выбранные файлы или папки в исходное место назначения и удалить" +" файлы или папки, которых нет в снимке. Будьте предельно осторожны, потому " +"что при этом будут удалены файлы и папки, исключенные при создании снимка." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Вы действительно хотите восстановить этот файл в новую папку?" +msgstr[1] "Вы действительно хотите восстановить эти файлы в новую папку?" +msgstr[2] "Вы действительно хотите восстановить эти файлы в новую папку?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Вы действительно хотите восстановить этот файл?" +msgstr[1] "Вы действительно хотите восстановить эти файлы?" +msgstr[2] "Вы действительно хотите восстановить эти файлы?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Пользовательский скрипт: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" +"В первой строке пользовательского скрипта должна быть строка, указывающая " +"интерпретатор (например, {example})." -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "Разработчики" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Показать/скрыть скрытые файлы и папки (Ctrl+H)" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "Переводы" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Включить" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "Лицензия" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Добавить в исключения" -#: ../../qt/logviewdialog.py:57 +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "Добавить в список исключений часто используемых элементы." +msgstr[1] "Добавить в список исключений часто используемых элементы." +msgstr[2] "Добавить в список исключений часто используемых элементы." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "Добавить в список исключений часто используемых элементы." +msgstr[1] "Добавить в список исключений часто используемых элементы." +msgstr[2] "Добавить в список исключений часто используемых элементы." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Установить язык" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Переведено: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Язык системы" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Использовать язык операционной системы." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Просмотр лога резервной копии" + +#: qt/logviewdialog.py:63 msgid "Last Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +msgstr "Просмотр последнего журнала" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Профиль:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Бекапы:" + +#: qt/logviewdialog.py:93 msgid "Filter:" msgstr "Фильтр:" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Ошибка, [I] Информация, [C] Изменение" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "декодировать пути" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" msgstr "Все" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "Ошибки" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 msgid "Changes" msgstr "Изменения" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "Оповещения" +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Ошибки" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] Ошибка, [I] Информация, [C] Изменение" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Информация" +msgstr[1] "Информация" +msgstr[2] "Информации" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "сбои передач rsync (экспериментально)" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Управление профилями" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Изменить" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Добавить" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "Ошибка" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Удалить" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "Вопрос" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Общие" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "Запустить BackInTime" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Включить" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Выполняется..." +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Исключить" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Сегодня" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "Удаление и хранение" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Вчера" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Параметры" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Эта неделя" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "Р&асширенные параметры" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "На прошлой неделе" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Восстановить конфигурацию" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Новый профиль" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Переименовать профиль" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "Показать весь лог" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Вы действительно хотите удалить профиль \"{name}\"?" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Изменить" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Копировать символьные ссылки в виде файлов" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Символьные ссылки сохраняются в резервную копию как реальные файлы или " +"папки. Выберите, будут ли копироваться все ссылки или только те, которые " +"указывают за пределы источника." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "Добавить" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Этот параметр может увеличить размер резервной копии." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "Удалить" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "По-умолчанию отключено." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Общие" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" +"Все символьные ссылки заменяются реальными файлами или папками, на которые " +"они указывают. Это увеличивает размер резервной копии и может привести к " +"многократному хранению одних и тех же файлов." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "Режим:" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "Применяется 'rsync --copy-links'." -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Только внешние" + +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"В виде файлов копируются только ссылки, указывающие за пределы источника " +"резервной копии. Это увеличивает размер резервной копии и может привести к " +"многократному хранению одних и тех же файлов." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "Место для сохранения резервных копий" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "Применяется 'rsync --copy-unsafe-links'." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "Папка" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Временные файлы редакторов и Офиса" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "Найстроки SSH" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Резервные копии Emacs" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "Хост:" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Файлы автосохранения Emacs" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "Порт:" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Своп файлы Vim" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "Пользователь:" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Временные файлы Microsoft Office" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "Путь:" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "Лок файлы LibreOffice и других редакторов OpenDocument" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Миниатюры и временные изображения" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "Приватный ключ:" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "Кэш миниатюр на GNU/Linux и других Юникс подобных ОС" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "Файл ключа" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "База данных миниатюр на Windows" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "Папка метаданных на macOS" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "Пароль" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Лок файлы некоторых приложений" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "Сохранить пароль в связку ключей" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Лок файлы приложения Discord" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "Закешировать пароль для Cron (Внимание: root может прочитать пароль)" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Лок файлы сессии Discord" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "Дополнительно" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Лок файлы Mozilla Firefox и Thunderbird" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Папки кеша и временных файлов" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Прикладной кэш пользователя" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Системная временная папка" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "Пакетный кэш дистрибутивов, основанных на Debian GNU/Linux" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Репозиторий Flatpak приложения и времени выпонения" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Системные каталоги времени выполнения" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Информация ядра и процессов" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Информация об устройствах и оборудовании (интерфейс sysfs)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Узлы устройств" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Системные файлы времени выполнения" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Прочее непостоянное" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Список подключенных в данный момент файловых систем" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Системный файл подкачки (виртуальная память)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "Точка монтирования виртуальной файловой системы GNOME" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Восстановленные объекты файловой системы" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Разное" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Папка метаданных на Microsoft Windows" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Пользовательская корзина" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Системные файлы резервных копий" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Рекомендуемые исключения" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "Выберите что исключить из резервной копии." + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "По умолчанию" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Сброс к предопределенному выбору" + +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Расписание" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "День:" +msgstr "Число:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" msgstr "День недели:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Часы:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Время:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" -msgstr "Часы:" +msgstr "Часа:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "после часа" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Минут:" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Запускать Back In Time как только диск подключен (только один раз каждые X " +"дней). Будет запрошен sudo-пароль." + +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Многократный запуск программы Back In Time. Это удобно если компьютер " +"работает нерегулярно." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" msgstr "Каждые:" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Записывать отладочные сообщения в журнал" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" +"Записать в системный журнал сообщения уровня отладки с помощью \"--debug\"." + +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" -"Запускать Back In Time как только устройство подключено (только раз в " -"несколько дней).\n" -"Будет запрошен sudo-пароль." +"Внимание: используйте временно для диагностики, так как генерируется большой" +" объем выходных данных." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Включить" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Отключено" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "Включить файлы и папки" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "При каждой загрузке системы" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Добавить файл" +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Каждую {n} минуту" +msgstr[1] "Каждые {n} минут" +msgstr[2] "Каждые {n} минут" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Каждый {n} час" +msgstr[1] "Каждые {n} часа" +msgstr[2] "Каждые {n} часов" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "Исключить" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Другой период (в часах)" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Каждый день" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "Исключить шаблоны, файлы или папки" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Периодически (anacron)" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "Очень рекомендуется:" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "При подключении носителя (udev)" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "Добавить по умолчанию" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Каждую неделю" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Каждый месяц" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Ежегодно" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Час(ов)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Дней (Дня)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Недель" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Месяцев" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Настроенные часы могут быть только списком часов, разделенных запятыми " +"(например, 8,12,18,23) или */3 для периодического резервного копирования " +"каждые 3 часа." -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Старее чем:" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" +#: qt/manageprofiles/sshkeyselector.py:65 +#, fuzzy +msgid "Choose an existing private key file from somewhere else." msgstr "" +"Vyberte existujúci súbor privátneho kľúča (zvyčajne s názvom \"id_ed25519\" " +"a v starších nastaveniach \"id_rsa\")." -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +#, fuzzy +msgid "Create a new SSH key without passphrase." +msgstr "Nepodarilo sa vytvoriť nový SSH kľúč v {path}." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, fuzzy, python-brace-format +msgid "Full path: {path}" +msgstr "Úplná cesta k snímke:" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "Privátny kľúč:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +#, fuzzy +msgid "Use system SSH configuration" +msgstr "Importovať konfiguráciu" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH Proxy" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Neodstraňovať pomenované snímky" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Server:" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Možnosti" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Povoliť upozornenia" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Používateľ:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Nevytvárať snímky pokiaľ systém beží na batérie" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." +msgstr "" +"Pripojiť sa k cieľovému hostiteľovi cez tento proxy server (známy aj ako " +"jump host). Podrobnosti nájdete v \"-J\" v dokumentácii príkazu \"ssh\" " +"alebo v \"ProxyJump\" v manuálovej stránke \"ssh_config\"." -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Nie je možné zistiť stav napájania" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Informácia{ENDBOLD}: V režime \"Šifrované cez SSH\" sú funkčné iba " +"jednoduché alebo dvojité hviezdičky (napr. {example2}). Ostatné typy " +"zástupných znakov a vzorov budú ignorované (napr. {example1}). Názvy súborov" +" sú v tomto režime nepredvídateľné kvôli šifrovaniu pomocou EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Vylúčiť vzory, súbory alebo adresáre" + +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Vylúčiť vzor" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Pridať súbor" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Pridať adresár" + +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "Otázka" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." msgstr "" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Vylúčiť súbor:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Vylúčiť súbory väčšie ako hodnota v {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 +#, fuzzy msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Pri vypnutom režime \"Úplný rsync režim\" to ovplyvní iba nové súbory, " +"pretože pre rsync je toto možnosť prenosu, nie možnosť vylúčenia. Preto " +"veľké súbory, ktoré boli predtým zálohované, zostanú v snímkach, aj keď boli" +" upravené." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Vylúčiť vzor" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Pokračovať pri chybách (zachovať nekompletné snímky)" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Vylúčiť vzor" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Úroveň záznamu:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Žiadne" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Vylúčiť súbor" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Zmeny a chyby" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Vylúčiť adresár" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Meňte tieto možnosti len pokiaľ naozaj viete, čo robíte!" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" +"Zakázané, pretože tento vzor nie je funkčný v režime \"Šifrované cez SSH\"." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Tieto možnosti sú pre pokročilé konfigurácie. Upravujte ich len vtedy, ak si" +" plne uvedomujete ich dôsledky." -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Spustiť 'rsync' s '{cmd}':" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "ako úloha cronu" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" - -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +msgstr "na vzdialenom hostiteľovi" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +#, fuzzy +msgid "when taking a manual backup" +msgstr "pri vytváraní manuálnej snímky" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Prosím, nainštalujte 'nocache' na povolenie tejto možnosti." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "na lokálnom počítači" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Presmerovať stdout do /dev/null v cron úlohách." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron automaticky odošle e-mail s pripojeným výstupom cron úloh, ak je " +"nainštalovaný MTA." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Presmerovať stderr do /dev/null v cron úlohách." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron automaticky odošle e-mail s pripojenými chybami cron úloh, ak je " +"nainštalovaný MTA." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/sek" + +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Obmedziť rýchlosť prenosu rsync:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "Zachovať ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Zachovať rozšírené atribúty (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "Kopírovať nezabezpečené odkazy (funguje len s absolútnymi odkazmi)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Obmedziť na jeden súborový systém" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Možnosti musia byť v úvodzovkách, napr. {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Vložte dodatočné možnosti pre rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Predpona na spustenie pred každým príkazom na vzdialenom hostiteľovi." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Premenné je potrebné escapovať pomocou \\$FOO. Toto sa netýka rsync. Takže " +"pre pridanie predpony pre rsync použite \"{example_value}\" s " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "predvolené" + +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" +msgstr "Pridať predponu k SSH príkazom" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Overiť, či je vzdialený hostiteľ online" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Upozornenie: Ak je táto možnosť vypnutá a vzdialený hostiteľ nie je " +"dostupný, môže to viesť k zvláštnym chybám." -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Overiť, či vzdialený hostiteľ podporuje všetky potrebné príkazy." + +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Upozornenie: Ak je táto možnosť vypnutá a vzdialený hostiteľ nepodporuje " +"všetky potrebné príkazy, môže to viesť k zvláštnym chybám." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(predvolené: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "zakázané" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "povolené" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Režim:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Kam sa uložia snímky" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH Nastavenia" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Cesta:" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "Nový profil" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Heslo" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Uložiť Heslo do Kľúčenky" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Uložiť Heslo do vyrovnávacej pamäte pre Cron (Bezpečnostný problém: root " +"môže čítať heslo)" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Pokročilé" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +#, fuzzy +msgid "Full backup path:" +msgstr "Úplná cesta k snímke:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Heslo sa nezhoduje." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Autenticita hostiteľa {host} nemôže byť overená." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "Odtlačok {keytype} kľúča je:" + +#: qt/manageprofiles/tab_general.py:613 +#, fuzzy +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Prosím, overte tento odtlačok. Chcete ho pridať do vášho súboru " +"'known_hosts'?" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Vytvoriť nový šifrovaný priečinok?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "SSH privátny kľúč" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Nový profil" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Premenovať profil" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Nepodarilo sa vytvoriť nový SSH kľúč v {path}." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Určite chcete vymazať profil \"%s\" ?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Zahrňuje súbory a adresáre" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, fuzzy, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." +msgstr "" +"\"{path}\" je symbolický odkaz. Prepojený cieľ nebude zálohovaný, pokiaľ ho tiež nezahrniete.\n" +"Chcete namiesto toho zahrnúť cieľ symbolického odkazu?" + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" msgstr "" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Zahrnúť súbor" + +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Zahrnúť adresár" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Povoliť upozornenia" + +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" +msgstr "Nevytvárať snímky pokiaľ systém beží na batérii" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Nie je možné zistiť stav napájania" + +#: qt/manageprofiles/tab_options.py:52 +#, fuzzy +msgid "Run only one backup at a time" +msgstr "Spustiť len jednu snímku naraz" + +#: qt/manageprofiles/tab_options.py:56 +#, fuzzy msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." +msgstr "" +"Ostatné snímky budú blokované, kým sa aktuálna snímka nedokončí. Toto je " +"globálna možnosť. Takže ovplyvní všetky profily pre tohto používateľa. Ale " +"musíte to aktivovať aj pre všetkých ostatných používateľov." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Zálohovať nahradené súbory pri obnovení" + +#: qt/manageprofiles/tab_options.py:78 +#, fuzzy +msgid "Continue on errors (keep incomplete backups)" +msgstr "Pokračovať pri chybách (zachovať nekompletné snímky)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Použiť kontrolný súčet pre zistenie zmien" + +#: qt/manageprofiles/tab_options.py:86 +#, fuzzy +msgid "Create a new backup whether there were changes or not." +msgstr "Vytvoriť novú snímku bez ohľadu na to, či došlo k zmenám." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" msgstr "" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:103 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Úroveň záznamu:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Žiadne" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "užívateľská príručka" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, fuzzy, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "" +"Nasledujúce pravidlá sa spracúvajú zhora nadol. Neskôr uvedené pravidlá " +"prepíšu skoršie a nie sú nimi obmedzené. Podrobnosti a príklady nájdete v " +"{manual}." + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Otvoriť používateľskú príručku v prehliadači." + +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Ponechať najnovšiu snímku." + +#: qt/manageprofiles/tab_remove_retention.py:241 +#, fuzzy +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Posledná alebo najnovšia snímka sa uchová za každých okolností." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Tento priebeh sa nedá zmeniť." + +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Ponechať pomenované snímky." + +#: qt/manageprofiles/tab_remove_retention.py:258 +#, fuzzy msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Snímky, ktorým bol okrem bežnej časovej pečiatky priradený aj názov, budú " +"uchované za každých okolností a nebudú odstránené." -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Roky" + +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Odstrániť snímky staršie ako" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Celé dni. Aktuálny deň sa ignoruje." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "" +"Kalendárne týždne s pondelkom ako prvým dňom. Aktuálny týždeň sa ignoruje." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12-mesačné obdobia. Aktuálny mesiac sa ignoruje." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Pravidlá uchovávania" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Spustiť na pozadí na vzdialenom hostiteľovi." + +#: qt/manageprofiles/tab_remove_retention.py:313 +#, fuzzy +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Procedúra inteligentného odstraňovania sa spustí priamo na vzdialenom " +"počítači, nie lokálne. Príkazy \"bash\", \"screen\" a \"flock\" musia byť " +"nainštalované a dostupné na vzdialenom počítači." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Ak je vybraté, Back In Time najprv otestuje vzdialený počítač." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dni sa počítajú od dnešného dňa." + +#: qt/manageprofiles/tab_remove_retention.py:322 +#, fuzzy +msgid "Keep all backups for the last" +msgstr "Uchovať všetky snímky za posledných" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "Počet dní." + +#: qt/manageprofiles/tab_remove_retention.py:334 +#, fuzzy +msgid "Keep the last backup for each day for the last" +msgstr "Uchovať poslednú snímku z každého dňa za posledných" + +#: qt/manageprofiles/tab_remove_retention.py:344 msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Týždne sa počítajú od aktuálneho prebiehajúceho týždňa. Týždeň začína v " +"pondelok." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Vylúčiť vzor" +#: qt/manageprofiles/tab_remove_retention.py:347 +#, fuzzy +msgid "Keep the last backup for each week for the last" +msgstr "Uchovať poslednú snímku z každého týždňa za posledných" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Vylúčiť súbor" +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "Týždne." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Vylúčiť priečinok" +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "" +"Mesiace sa počítajú ako kalendárne mesiace, počnúc aktuálnym mesiacom." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Zahrnúť súbor" +#: qt/manageprofiles/tab_remove_retention.py:360 +#, fuzzy +msgid "Keep the last backup for each month for the last" +msgstr "Uchovať poslednú snímku z každého mesiaca za posledných" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "mesiac(e)." + +#: qt/manageprofiles/tab_remove_retention.py:370 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"The years are counted as calendar years starting with the current year." +msgstr "Roky sa počítajú ako kalendárne roky, počnúc aktuálnym rokom." + +#: qt/manageprofiles/tab_remove_retention.py:372 +#, fuzzy +msgid "Keep the last backup for each year for" +msgstr "Uchovať poslednú snímku z každého roka za" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "všetky roky." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… voľný priestor je menší ako" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… voľné i-uzly sú menšie ako" + +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Odstrániť najstaršie snímky, ak …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Zahrnúť priečinok" +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Ste si istí, že chcete zmeniť priečinok snímok?" +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Skratky" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/placeswidget.py:96 +msgid "Places" msgstr "" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/placeswidget.py:97 +msgid "File System" msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Adresár záloh" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Zobraziť posledný log" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Spustiť {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Prebieha…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importovať konfiguráciu" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importovať" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Prebieha…" + +#: qt/restoreconfigdialog.py:211 +#, fuzzy, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Vyberte adresár snímky, z ktorého sa má importovať konfiguračný súbor. Cesta" +" môže vyzerať napríklad takto: {samplePath}" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/restoreconfigdialog.py:216 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Ak sa adresár nachádza na externom alebo vzdialenom disku, musí byť predtým " +"manuálne pripojený." -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" msgstr "" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Ukázať skryté súbory" + +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Zahrňuje súbory a adresáre" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Vytvorenie súboru v tomto adresári zlyhalo:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." msgstr "" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Zobraziť celý záznam" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Možnosti rozdielov" +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Vypnúť systém po dokončení snímky." + +#: qt/shutdowndlg.py:36 +#, fuzzy +msgid "Cancel Shutdown" +msgstr "Vypnúť" + +#: qt/shutdowndlg.py:37 +#, fuzzy +msgid "Shutdown Now" +msgstr "Vypnúť" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" -#: ../../qt/snapshotsdialog.py:60 +#: qt/snapshotsdialog.py:61 +#, fuzzy +msgid "Options about comparing backups" +msgstr "Možnosti porovnávania snímok" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Príkaz:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametre:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "Použiť %1 a %2 parametre cesty" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Zoznam iba rozdielnych snímok" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Prosím, nastavte príkaz diff alebo stlačte Zrušiť." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Príkaz \"{cmd}\" sa v tomto systéme nenašiel. Skúste niečo iné alebo stlačte" +" Zrušiť." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Pre príkaz diff neboli nastavené žiadne parametre. Používa sa predvolená " +"hodnota \"{params}\"." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +#, fuzzy +msgid "Backups" +msgstr "&Záloha" + +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Iba odlišné snímky" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:159 +#, fuzzy +msgid "List only backups that are equal to:" +msgstr "Zoznam snímok, ktoré sú rovné:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Hĺbková kontrola (presnejšia, ale pomalšia)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Vymazať" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Vybrať všetko" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Rozdiel" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Porovnať" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Ísť na" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Nemôžete porovnávať snímku s ňou samou" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Možnosti" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Príkaz nenájdený: %s" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" + +#: qt/snapshotsdialog.py:470 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Naozaj chcete odstrániť {file} zo snímky {snapshot_id}?" + +#: qt/snapshotsdialog.py:475 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Naozaj chcete odstrániť {file} v {count} snímkach?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "UPOZORNENIE: Nie je možné odvolať." + +#: qt/snapshotsdialog.py:495 +#, fuzzy, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Vylúčiť {path} z budúcich snímok?" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Koreňový priečinok" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Dnes" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Včera" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Tento týždeň" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Minulý týždeň" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Posledná kontrola {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#, fuzzy +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Toto NIE JE snímka, ale živý náhľad vašich lokálnych súborov" diff --git a/common/po/sl.po b/common/po/sl.po index 728ecc8f2..2c30fc230 100644 --- a/common/po/sl.po +++ b/common/po/sl.po @@ -1,1646 +1,2568 @@ +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Liam Starič (ravijol1) +# SPDX-FileCopyrightText: © Vanja Cvelbar +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: Back In Time 0.9.10\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2018-02-14 11:38+0000\n" -"Last-Translator: Matic Gradišer \n" -"Language-Team: \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-01-28 13:53+0000\n" +"Last-Translator: ravijol1 \n" +"Language-Team: Slovenian \n" +"Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || " -"n%100==4 ? 3 : 0);\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);\n" +"X-Generator: Weblate 5.15.2\n" "X-Poedit-Country: SLOVENIA\n" "X-Poedit-Language: Slovenian\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Opozorilo" + +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Glavni profil" + +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Lokalno šifrirano (EncFC)" + +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH šifrirano (EncFS)" + +#: common/config.py:237 +msgid "Local" +msgstr "Lokalno" + +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Lokalno šifrirano" + +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Šifriranje" + +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" + +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Privatni ključ SSH" + +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil: \"{name}\"" + +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Mapa z varnostnimi kopijami ni veljavna." + +#: common/config.py:313 +msgid "At least one directory must be selected for backup." msgstr "" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "Profil \"%s\" Že obstaja !" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Mapa: {path}" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Tega direktorija ni mogoče vključiti v varnostno kopijo, ker je del samega " +"cilja varnostne kopije." -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Onemogočeno" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Ob vsakem zagonu/ponovnem zagonu" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Vsakih 5 minut" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Udev razpored ne deluje z načinom {mode}" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Vsakih 10 minut" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Novega crontaba ni mogoče zapisati." -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "Vsakih 30 minut" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Vsako uro" +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Konfiguracije ni bilo mogoče shraniti" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "Vsaki 2 uri" +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Konfiguracije ni bilo mogoče naložiti" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "Vsake 4 ure" +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Profil \"{name}\" že obstaja." -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "Vsakih 6 ur" +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Zadnjega profila ni mogoče odstraniti." -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "Vsakih 12 ur" +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Ni mogoče vmestiti \"{command}\"" -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Specifične ure" +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Konfiguracije za šifrirano mapo ni bilo mogoče najti." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Vsak dan" +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Želite ustvariti novo šifrirano mapo?" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "Ponavljajoče (anacron)" +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Prekliči" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Ko se priključi naprava (udev)" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Prosimo, da za potrditev še enkrat vpišete geslo za EncFS." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Vsak teden" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFS gesla se ne ujemajo." -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Vsak mesec" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Ustvari posnetek" + +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Ni bilo mogoče inicializirati pot \"{command}\"" + +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "{mountprocess} ni bilo mogoče odklopiti iz {mountpoint}." + +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "{command} ni mogoče najti. Namestite ga (npr. z \"{installcommand}\")" + +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Točka priklopa {mntpoint} ni prazna." + +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Vpišite geslo za profil {mode} \"{profile}\":" + +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Ni bilo mogoče namestiti pravila Udev za profil {profile_id}. Storitev DBus " +"'{dbus_interface}' ni bila na voljo." -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Dni" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Ni bilo mogoče najti UUID za {path}" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Tednov" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "SPODLETELO" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Let" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Obnovi dovoljenja" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Ura(Ure)" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Končano" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Mesec(ev)" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " ESKPERIMENTALNO!" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Odložitev varnostnega kopiranja med delovanjem na bateriji" -#: ../../common/config.py:129 -msgid "Local" -msgstr "Lokalno" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Mape za varnostne kopije ni bilo mogoče najdi." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Če je na izmenljivem pogonu, ga priključite." -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Privatni SSH ključ" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Čakanje {n} sekund." +msgstr[1] "Čakanje {n} sekundo." +msgstr[2] "Čakanje {n} sekundi." +msgstr[3] "Čakanje {n} sekunde." -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Lokalno šifrirano" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Ustvarjanje varnostne kopije {snapshot_id} ni uspelo." -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Šifriranje" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "SSH šifrirano" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Mape ni mogoče ustvariti." -#: ../../common/config.py:135 -msgid "Default" -msgstr "Privzeto" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Shranjevanje konfiguracijske datoteke…" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Shranjevanje dovoljenj…" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "" +"Najdena nedokončana varnostna kopija {snapshot_id}, ki jo je mogoče " +"nadaljevati." -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Odstranjevanje nedokončane {snapshot_id} mape od zadnjega zagona" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Mape ni bilo mogoče odstraniti" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Ustvarjane varnostne kopije" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Uspeh" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Delni prenos zaradi napake" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "Delni prenos zaradi izginulih izvornih datotek (glej 'man rsync')" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' se je končal z izhodno kodo {exit_code}" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Za več informacij glej 'man rsync'" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "" +"Negativne izhodne kode rsync so signalne številke, glej 'kill -l' in 'man " +"kill'" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Nič se ni spremenilo, nova varnostna kopija ni potrebna" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Glavni profil" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "{new_path} ni mogoče preimenovati v {path}." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil: \"%s\"" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Uveljavljanje pravil za odstranjevanje starih varnostnih kopij" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" +#: common/snapshots.py:2031 +msgid "Applying retention policy" msgstr "" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Poskus ohranjanja minimalnega prostora" + +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Poskus ohranjanja najmanj {perc} prostih inodov" + +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Zdaj" + +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "{sshfs} ni bilo mogoče vmestiti" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" +#: common/sshtools.py:306 +#, fuzzy +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent ni bil najden. Prepričajte se, da je nameščen." + +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"Zasebnega ključa ssh ni bilo mogoče odkleniti. Napačno geslo ali geslo ni na" +" voljo za cron." + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Oddaljena pot obstaja, ampak ni mapa." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Oddaljena pot ni zapisljiva." -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Oddaljena pot ni izvršljiva." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Oddaljene poti ni bilo mogoče ustvariti." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Oddaljeni gostitelj {host} ne podpira {command}" + +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Ukazi za preverjanje na gostitelju {host} so vrnili neznano napako" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Oddaljeni gostitelj {host} ne podpira trdih povezav" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Kopiraj javni ssh-ključ \"{pubkey}\" na oddaljenega gostitelja \"{host}\"." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Prosim vpišite geslo za \"{user}\"." + +#: common/tools.py:382 +#, fuzzy, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"Ciljni datotečni sistem za {path} je formatiran s FAT, ki ne podpira trdih " +"povezav. Uporabite lastni datotečni sistem Linux." -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s ni mapa !" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "Oddaljena pot {path} obstaja, ampak ni mapa." -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" +#: common/tools.py:428 +msgid "Creation of following directory failed:" msgstr "" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." msgstr "" -#: ../../common/config.py:402 -#, python-format +#: common/tools.py:471 +#, fuzzy, python-brace-format msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"Ciljni datotečni sistem za {path} je formatiran s FAT, ki ne podpira trdih " +"povezav. Uporabite lastni datotečni sistem Linux." -#: ../../common/config.py:407 -#, python-format +#: common/tools.py:482 +#, fuzzy, python-brace-format msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"Ciljni datotečni sistem za {path} je SMB priklopjen v skupno rabo. " +"Preverite, ali oddaljeni strežnik SMB podpira simbolne povezave ali " +"aktivirajte {copyLinks} v {expertOptions}." -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 +#: common/tools.py:486 msgid "Copy links (dereference symbolic links)" -msgstr "" +msgstr "Kopiraj povezave (dereferenciraj simbolne povezave)" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 +#: common/tools.py:487 msgid "Expert Options" msgstr "Napredne možnosti" -#: ../../common/config.py:413 -#, python-format +#: common/tools.py:491 +#, fuzzy, python-brace-format msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"Ciljni datotečni sistem za {path} je priklopljen v skupno rabo s protokolom " +"sshfs . Sshfs ne podpira trdih povezav. Namesto tega uporabite način 'SSH'." -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." +#: common/tools.py:525 +msgid "File creation failed in this directory:" msgstr "" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "O Back In Time" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Avtorske pravice:" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Avtorji:" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Prevajalci:" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" +"Liam Starič (ravijol1) \n" +"Vanja Cvelbar " -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." msgstr "" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "ta povezava" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "Prekliči" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Spletno mesto projekta" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Navodila za uporabo" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" -#: ../../common/encfstools.py:161 +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Različica{BOLDEND}: {version}" + +#: qt/app.py:332 +#, fuzzy, python-brace-format msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"Zgleda, da ste prvič zagnali {app_name}, ker ni mogoče najti konfiguracije." -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Naredi posnetek" - -#: ../../common/mount.py:415 -#, python-format +#: qt/app.py:337 +#, fuzzy msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Uvozi obstoječo konfiguracijo (iz imenika varnostne kopije ali iz drugega " +"računalnika)?" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Potem pritisnite OK." + +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Ustvari varnostno kopijo" + +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Za zaznavanje datotečnih sprememb uporabite čas in velikost." + +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "Ustvari posnetek (način checksum)" + +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Za zaznavanje datotečnih sprememb uporabite checksum." + +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Začasno ustavi postopek varnostnega kopiranja" + +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Nadaljuj postopek varnostnega kopiranja" + +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Ustavi postopek varnostega kopiranja" + +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Osveži seznam varnostnih kopij" + +#: qt/app.py:508 +msgid "Name backup" +msgstr "Imenuj varnostno kopijo" + +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Odstrani varnostno kopijo" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Ogled dnevnika varnostnih kopij" + +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "Ne odstranjuj posnetkov z imenom." + +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "Odpri zadnji dnevnik varnostnih kopij" + +#: qt/app.py:522 +msgid "View log of the latest backup." msgstr "" -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Upravljanje profilov…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Uredi user-callback" + +#: qt/app.py:532 +msgid "Shutdown" +msgstr "Zaustavi" + +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." +msgstr "Zaustavite sistem, ko je posnetek končan." + +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Nastavitev jezika…" + +#: qt/app.py:540 +msgid "Exit" +msgstr "Izhod" + +#: qt/app.py:550 +#, fuzzy +msgid "man page: Back In Time" +msgstr "Back In &Time" + +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" msgstr "" -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." +#: qt/app.py:555 +#, fuzzy +msgid "man page: Profiles config file" +msgstr "Konfiguracijska datoteka profilov" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" msgstr "" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "Dnevnik sprememb" + +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +#: qt/app.py:573 +msgid "FAQ" +msgstr "Pogosta vprašanja" + +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" msgstr "" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "SPODLETELO" +#: qt/app.py:577 +msgid "Ask a question" +msgstr "Postavite vprašanje" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" +#: qt/app.py:581 +msgid "Report a bug" +msgstr "Prijavite napako" + +#: qt/app.py:584 +msgid "Translation" +msgstr "Prevod" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." msgstr "" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Narejeno" +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Prehod šifriranja (EncFS)" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." msgstr "" -#: ../../common/snapshots.py:669 +#: qt/app.py:599 +msgid "About" +msgstr "O programu" + +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Obnovi" + +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "Obnovi izbrane datoteke ali mape v prvotni cilj." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Obnovi v …" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "Obnovi izbrane datoteke ali mape v nov cilj." + +#: qt/app.py:615 +#, fuzzy msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "Obnovi trenutno prikazano mapo in vso njeno vsebino v prvotni cilj." -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "" -msgstr[1] "" +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "Obnovi trenutno prikazano mapo in vso njeno vsebino v nov cilj." -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "Gor" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Zaključevanje" +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Pokaži skrite datoteke" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Primerjaj varnostne kopije…" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" msgstr "" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." msgstr "" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&VarnostnaKopija" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Obnovi" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "&Pomoč" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Naredi posnetek" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Systray ikona" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: qt/app.py:780 +msgid "Automatic" +msgstr "Samodejno" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "Svetla ikona" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Temna ikona" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Samo ikone" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" +#: qt/app.py:811 +msgid "Text only" +msgstr "Samo besedilo" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Besedilo pod ikonami" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Besedilo zraven ikone" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Ta mapa ne obstaja\n" +"v trenutno izbranem posnetku." -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" +"Ta mapa ne obstaja\n" +"v trenutno izbranem posnetku." -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" +#: qt/app.py:995 +#, fuzzy +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Če zaprete to okno, Back In Time ne bo mogel ugasniti vašega sistema, ko se " +"posnetek stanja zaključi." -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" +#: qt/app.py:998 +msgid "Close the window anyway?" msgstr "" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Narejeno, varnostna kopija ni potrebna" + +#: qt/app.py:1260 +msgid "Working:" +msgstr "V obdelavi:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "V obdelavi" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Napaka" + +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "Poslano:" + +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "Hitrost:" + +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "ETA:" + +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Varnostna kopija:" + +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Obnovi {path}" + +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Obnovi {path} v …" + +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Pozdravljeni!\n" +"Back In Time v jeziku {language} ste do zdaj že nekajkrat uporabljali.\n" +"Prevod vaše nameščene verzije Back In Time v {language} je {perc} dokončan. Ne glede na vaš nivo na tehničnega znanja, lahko prispevate k prevodu in s tem k samemu projektu Back In Time.\n" +"Če želite prispevati, obiščite {translation_platform_url}. Za dodatno pomoč in vprašanja, prosimo obiščite {back_in_time_project_website}.\n" +"Opravičujemo se za prekinitev, to sporočilo ne bo več prikazano. To pogovorno okno je kadar koli na voljo prek menija za pomoč.\n" +"Vaša ekipa Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "prevajalna platforma" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Spletna stran" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Vaš prevod" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." msgstr "" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." msgstr "" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Zdaj" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Mailing lista {link_and_label}." -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." msgstr "" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Prijavi napako" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." msgstr "" -#: ../../common/sshtools.py:345 -#, python-format +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +#: qt/app.py:1841 +msgid "Proceed with the backup?" msgstr "" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" msgstr "" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" msgstr "" -#: ../../common/sshtools.py:470 -#, python-format +#: qt/app.py:1875 +#, fuzzy, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}POZOR{BOLDEND}: Brisanje datotek v korenskem datotečnem sistemu lahko " +"pokvari ves sistem." -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Ime varnostne kopije" + +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "Nastavitve jezika se uveljavijo po ponovnem zagonu Back In Time." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" msgstr "" -#: ../../common/sshtools.py:474 -#, python-format +#: qt/backintime-qt-root.desktop:23 msgid "" -"Couldn't create remote path:\n" -" %s" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Verzijske varnostne kopije" + +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Vprašanje" + +#: qt/confirmrestoredialog.py:76 +#, python-brace-format msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Pred prepisom ali odstranitvijo lokalnih datotek, ustvarite varnostne kopije" +" s pripono {suffix}." -#: ../../common/sshtools.py:686 -#, python-format +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Imenom novejših različic datotek bo pred obnovitvijo dodana pripona " +"{suffix}. Če jih ne potrebujete več, jih lahko odstranite z naslednjim " +"ukazom:" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Obnovi le datoteke, ki ne obstajajo ali so novejše od tistih na ciljni " +"lokaciji. Uporablja možnost \"{rsync_example}\"." + +#: qt/confirmrestoredialog.py:133 +#, fuzzy +msgid "Remove newer elements in original directory." +msgstr "Odstrani novejše datoteke v prvotni mapi." -#: ../../common/sshtools.py:761 -#, python-format +#: qt/confirmrestoredialog.py:135 +#, fuzzy msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Obnovite izbrane datoteke ali mape na prvotno ciljno lokacijo in odstranite " +"datoteke ali mape, ki jih ni v posnetku. Bodite zelo previdni, saj bo to " +"odstranilo datoteke in mape, ki so bile izključene med ustvarjanjem " +"posnetka." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "Ali res želite obnoviti te elemente v novo mapo?" +msgstr[1] "Ali res želite obnoviti ta element v novo mapo?" +msgstr[2] "Ali res želite obnoviti ta elementa v novo mapo?" +msgstr[3] "Ali res želite obnoviti te elemente v novo mapo?" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Ali res želite obnoviti te elemente?" +msgstr[1] "Ali res želite obnoviti ta element?" +msgstr[2] "Ali res želite obnoviti ta elementa?" +msgstr[3] "Ali res želite obnoviti te elemente?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Vključi datoteke in mape" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Dodaj v Vključi" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Dodaj v Izključi" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Nastavitev jezika" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Prevedeno: {percent}" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Sistemsko privzeto" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "Uporabi jezik operacijskega sistema." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Dnevnik varnostnih kopij" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Zadnji dnevnik" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "Profil:" + +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Varnostne kopije:" + +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "Filter:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Napake, [I] Informacije, [C] Spremembe" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "dekodiranje poti" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "Vse" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Spremembe" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Napake" + +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informacij" +msgstr[1] "Informacija" +msgstr[2] "Informaciji" +msgstr[3] "Informacije" + +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "Neuspešni prenosi rsync (eksperimentalno)" + +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Upravljanje profilov" + +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Uredi" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Dodaj" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Odstrani" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Splošno" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Vključitev" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Izključitev" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" msgstr "" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Ime posnetka" +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Možnosti" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "&Napredne nastavitve" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Obnovi konfiguracijo" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Nov profil" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Preimenuj profil" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Odstrani posnetek" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Odstrani profil \"{name}\"?" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Nastavitve" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "" -#: ../../qt/app.py:142 -msgid "Shutdown" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." msgstr "" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "Izhod" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Pomoč" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Samo zunanje" -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/app.py:163 -msgid "Website" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" msgstr "" -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Varnostne kopije Emacs datotek" -#: ../../qt/app.py:169 -msgid "Ask a question" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs autosave datoteke" + +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" msgstr "" -#: ../../qt/app.py:171 -msgid "Report a bug" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" msgstr "" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "O programu" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "Gor" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "Pokaži skrite datoteke" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Obnovi" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" msgstr "" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" msgstr "" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Mape za varnostno kopijo" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" msgstr "" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Posnetki" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Sistemska začasna mapa" -#: ../../qt/app.py:260 -msgid "Snapshot" +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/app.py:271 -msgid "View" +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" msgstr "" -#: ../../qt/app.py:321 -msgid "Shortcuts" +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Pokaži skrite datoteke" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" -#: ../../qt/app.py:398 -msgid "Add to Include" +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Narejeno, varnostna kopija ni potrebna" +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Razno" -#: ../../qt/app.py:723 -msgid "Error:" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" msgstr "" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Varnostne kopije sistemskih datotek" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Izključi predloge" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Privzeto" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Splošno" +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" +msgstr "Časovni razpored" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Koren" +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" +msgstr "Dan:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "Dom" +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" +msgstr "Dan v tednu:" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Čas:" -#: ../../qt/app.py:942 -#, python-format +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" +msgstr "Ur:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "po tej uri" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minute:" + +#: qt/manageprofiles/schedulewidget.py:93 msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" -"Res želite zbrisati posnetek:\n" -"%s" +"Zaženi Back In Time takoj, ko se disk poveže (zgolj enkrat na X dni). Od vas" +" bo zahtevan vpis sudo gesla." -#: ../../qt/app.py:1025 -#, python-format +#: qt/manageprofiles/schedulewidget.py:98 msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." msgstr "" +"Zaženi Back In Time večkrat. To služi za primer, ko računalnik ni redno " +"prižgan." -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" +msgstr "Vsakih:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Omogoči beleženje razhroščevalnih sporočil" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Zapiše razhroščevalna poročila v sistemsko beleženje z uporabo \"--debug\"." -#: ../../qt/app.py:1038 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Pozor: uporabite samo začasno za odpravljanje napak, ker ustvari zelo veliko" +" podatkov." -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Onemogočeno" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Ob vsakem zagonu/ponovnem zagonu" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Vsakih {n} minut" +msgstr[1] "Vsako {n} minuto" +msgstr[2] "Vsake {n} minute" +msgstr[3] "Vsake {n} minute" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Vsakih {n} ur" +msgstr[1] "Vsakih {n} uro" +msgstr[2] "Vsaki {n} uri" +msgstr[3] "Vsake {n} ure" + +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Ure po meri" + +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Vsak dan" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Ponavljajoče (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Ko se priključi disk (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Vsak teden" -#: ../../qt/app.py:1072 +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Vsak mesec" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Vsako leto" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Ura/ur" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Dan/dni" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Teden/tednov" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Mesec/mesecev" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Ure po meri so lahko samo seznam ur, ločenih z vejicami (npr. 8,12,18,23) " +"ali */3 za občasne varnostne kopije vsake 3 ure." -#: ../../qt/app.py:1083 -#, python-format -msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" +#: qt/manageprofiles/sshkeyselector.py:65 +#, fuzzy +msgid "Choose an existing private key file from somewhere else." msgstr "" +"Изаберите постојећи приватни кључ фајл (обично именован \"id_ed25519\" а у " +"старијим подешавањима \"id_rsa\")." -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" msgstr "" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +#, fuzzy +msgid "Create a new SSH key without passphrase." +msgstr "Креирање новог SSH кључа у {path} није успело." -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:92 +#, fuzzy, python-brace-format +msgid "Full path: {path}" +msgstr "Пуна путања снимка:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +#, fuzzy +msgid "Private key:" +msgstr "Приватни Кључ:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:210 +#, fuzzy +msgid "Use system SSH configuration" +msgstr "Увези конфигурацију" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Nemoj ukloniti imenovane snimke" - -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Opcije" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH прокси" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Omogući obaveštenja" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Домаћин:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Onemogući snimke kada se računar napaja iz baterije" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Порт:" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Informacija o status napajanja nije dostupna od sistema" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Корисник:" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Повежите се са циљним домаћином преко овог проксија (познатог и као jump " +"host). Погледајте \"-J\" у документацији за \"ssh\" команду или " +"\"ProxyJump\" у \"ssh_config\" man страници за детаље." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Информације{ENDBOLD}: У 'SSH шифровано' режиму, само једноструке или " +"двоструке звездице су функционалне (нпр. {example2}). Други типови џокера и " +"шаблона ће бити игнорисани (нпр. {example1}). Имена датотека су непредвидива" +" у овом режиму због шифровања од стране EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Изоставите шаблоне, фајлове или директоријуме" + +#: qt/manageprofiles/tab_exclude.py:102 +#, fuzzy +msgid "Add pattern" +msgstr "Изостави шаблон" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +#, fuzzy +msgid "Add files" +msgstr "Додај фајл" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +#, fuzzy +msgid "Add directories" +msgstr "Додајте директоријум" + +#: qt/manageprofiles/tab_exclude.py:118 +#, fuzzy +msgid "Suggestions" +msgstr "Питање" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "" + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Изостави фајлове веће од:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Изостави фајлове веће од вредности у {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 +#, fuzzy msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Са онемогућеним 'Full rsync mode', ово ће утицати само на нове фајлове јер " +"је за rsync ово опција преноса, а не опција изостављања. Стога, велики " +"фајлови који су претходно резервно копирани ће остати у снимцима чак и ако " +"су модификовани." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Изостави шаблон" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Nastavi nakon greške (zadrži nekompletan snimak)" +#: qt/manageprofiles/tab_exclude.py:267 +#, fuzzy +msgid "Enter an exclude pattern:" +msgstr "Изостави шаблон" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." msgstr "" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Nivo detalja u izveštaju:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Nijedan" +#: qt/manageprofiles/tab_exclude.py:303 +#, fuzzy +msgid "Exclude files" +msgstr "Изостави фајл" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Izmene i greške" +#: qt/manageprofiles/tab_exclude.py:316 +#, fuzzy +msgid "Exclude directories" +msgstr "Изоставите директоријум" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Izmeni ove opcije samo ako znaš šta radiš!" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "Деактивирано зато што шаблони не функционишу у 'SSH шифрованом' моду." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Ове опције су за напредне конфигурације. Измените само ако сте у потпуности " +"свесни њихових импликација." -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Покрените 'rsync' са '{cmd}':" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "као cron посао" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" - -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +msgstr "на удаљеном домаћину" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +#, fuzzy +msgid "when taking a manual backup" +msgstr "када се креира ручни снимак" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Молимо вас да инсталирате 'nocache' да бисте омогућили ову опцију." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "на локалној машини" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Преусмерите stdout ка /dev/null у cron пословима." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Cron ће аутоматски послати email са приложеним излазом cronjob-ова уколико " +"је MTA инсталиран." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Преусмерите stderr према /dev/null у cron пословима." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Cron ће аутоматски послати email са приложеним грешкама од cronjob-ова " +"уколико је MTA инсталиран." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/сек" + +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Ограничите употребу пропусног опсега rsync-а:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" -msgstr "Sačuvaj ACL" +msgstr "Сачувај ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" -msgstr "Sačuvaj proširene atribute (xattr)" +msgstr "Сачувај проширене атрибуте (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "Kopiraj nebezbedni link (funkcioniše samo sa apsolutnim linkovima)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Ограничите на само један фајл систем" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Опције морају бити под наводницима нпр. {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Налепи додатне опције за rsync" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Префикс који ће се покретати пре сваке команде на удаљеном домаћину." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Променљиве треба искочити са \\$FOO. Ово не дотиче rsync. Тако да би додали " +"префикс за rsync користите \"{example_value}\" са {rsync_options_value}." -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "подразумевано" + +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" +msgstr "Додај префикс SSH командама" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Провери да ли је удаљени домаћин онлајн" + +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Упозорење: уколико је онемогућено и удаљени домаћин није доступан, ово може " +"довести до неких чудних грешака." + +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Проверите да ли удаљени домаћин подржава све неопходне команде." -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Упозорење: уколико је онемогућено и удаљени домаћин не подржава све " +"неопходне команде, ово може довести до неких чудних грешака." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(подразумевано: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "онемогућено" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "омогућено" + +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Мод:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +#, fuzzy +msgid "Where to save backups" +msgstr "Где да чувам снимке" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH Поставке" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Путања:" + +#: qt/manageprofiles/tab_general.py:139 +#, fuzzy +msgid "Key file:" +msgstr "Нови профил" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Лозинка" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Сачувај лозинку у Keyring" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" +"Кеширај лозинку за Cron (Безбедносни проблем: root може читати лозинку)" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Напредно" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +#, fuzzy +msgid "Full backup path:" +msgstr "Пуна путања снимка:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:404 +#, fuzzy +msgid "The encryption password cannot be empty." +msgstr "Лозинка се не поклапа." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" msgstr "" -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Аутентичност домаћина {host} се не може утврдити." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} отисак прста кључа је:" + +#: qt/manageprofiles/tab_general.py:613 +#, fuzzy +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" +"Молимо проверите овај отисак прста. Да ли желите да га додате у своју " +"датотеку 'known_hosts'?" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" +#: qt/manageprofiles/tab_general.py:709 +#, fuzzy +msgid "Really change the backup directory?" +msgstr "Да ли желите да креирате нови шифровани директоријум?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "" + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." msgstr "" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:756 +#, fuzzy +msgid "Invalid file: Not a private SSH key" +msgstr "SSH приватни кључ" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Novi profil" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Preimenuj profil" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Креирање новог SSH кључа у {path} није успело." -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Da li ste sigurni da želite da uklonite profil \"%s\" ?" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Укључи фајлове и директоријуме" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_include.py:168 +#, fuzzy, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"\"{path}\" је симболичка веза. Повезана мета неће бити резервно копирана док је не укључите.\n" +"Да ли желите да укључите мету симболичке везе?" + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Укључити циљ уместо симболичке везе?" + +#: qt/manageprofiles/tab_include.py:180 +#, fuzzy +msgid "Include files" +msgstr "Укључи фајл" + +#: qt/manageprofiles/tab_include.py:199 +#, fuzzy +msgid "Include directories" +msgstr "Укључите директоријум" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Омогући обавештења" + +#: qt/manageprofiles/tab_options.py:43 +#, fuzzy +msgid "Disable backups when on battery" +msgstr "Онемогући снимке када се рачунар напаја из батерије" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Информација о статусу напајања није доступна од система" + +#: qt/manageprofiles/tab_options.py:52 +#, fuzzy +msgid "Run only one backup at a time" +msgstr "Покрени само један снимак у исто време" -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_options.py:56 +#, fuzzy msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Остали снимци ће бити блокирани док се тренутни снимак не заврши. Ово је " +"глобална опција. Тако да ће утицати на све профиле за овог корисника. Али " +"ово морате активирати и за све остале кориснике такође." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Резервна копија је заменила фајлове приликом враћања" + +#: qt/manageprofiles/tab_options.py:78 +#, fuzzy +msgid "Continue on errors (keep incomplete backups)" +msgstr "Настави након грешке (задржи непотпун снимак)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Користи контролни број за примећивање промена" + +#: qt/manageprofiles/tab_options.py:86 +#, fuzzy +msgid "Create a new backup whether there were changes or not." +msgstr "Направи нови снимак без обзира да ли је било промена или не." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" msgstr "" -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:103 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." msgstr "" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Ниво детаља у извештају:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Ниједан" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "приручник за кориснике" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, fuzzy, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Следећа правила се обрађују од врха до дна. Каснија правила надјачавају " +"ранија и нису ограничена њима. Погледајте {manual} за детаље и примере." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Izostavi šablon" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Отворите приручник за кориснике у прегледачу." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Izostavi fajl" +#: qt/manageprofiles/tab_remove_retention.py:237 +#, fuzzy +msgid "Keep the most recent backup." +msgstr "Чувајте именоване снимке." -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Izostavi direktorijum" +#: qt/manageprofiles/tab_remove_retention.py:241 +#, fuzzy +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Последњи или најсвежији снимак се чува под свим околностима." -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Uključi fajl" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "То понашање не може бити промењено." -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:255 +#, fuzzy +msgid "Keep named backups." +msgstr "Чувајте именоване снимке." + +#: qt/manageprofiles/tab_remove_retention.py:258 +#, fuzzy msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Снимци који, поред уобичајене временске ознаке, имају дато име неће бити " +"обрисани." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "Година" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Uključi direktorijum" +#: qt/manageprofiles/tab_remove_retention.py:278 +#, fuzzy +msgid "Remove backups older than" +msgstr "Уклони снимак" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Da li stvarno želiš da promeniš direktorijum za snimkea?" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Пуни дани. Текући дан се игнорише." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Календарске недеље са понедељком као првим даном. Текућа недеља се игнорише." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12-месечни периоди. Текући месец се игнорише." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Политика задржавања" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Покрени у позадини на удаљеном домаћину." + +#: qt/manageprofiles/tab_remove_retention.py:313 +#, fuzzy +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Процедура паметног уклањања ће се извршити директно на удаљеној машини, а не" +" локално. Команде \"bash\", \"screen\" и \"flock\" морају бити инсталиране и" +" доступне на удаљеној машини." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Ако је изабрано, Back In Time ће прво тестирати удаљену машину." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Дани се броје од данас." + +#: qt/manageprofiles/tab_remove_retention.py:322 +#, fuzzy +msgid "Keep all backups for the last" +msgstr "Задржи све снимке за последњи" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "дан(а)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +#, fuzzy +msgid "Keep the last backup for each day for the last" +msgstr "Задржи један снимак дневно за последњи" + +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "Недеље се броје од тренутне недеље. Недеља почиње у понедељак." + +#: qt/manageprofiles/tab_remove_retention.py:347 +#, fuzzy +msgid "Keep the last backup for each week for the last" +msgstr "Задржи један снимак недељно за последњи" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "недеља/е." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Месеци се броје као календарски месеци, почевши од текућег месеца." + +#: qt/manageprofiles/tab_remove_retention.py:360 +#, fuzzy +msgid "Keep the last backup for each month for the last" +msgstr "Задржи један снимак месечно за последњи" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "месец(и)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Године се броје као календарске године, почевши од текуће године." + +#: qt/manageprofiles/tab_remove_retention.py:372 +#, fuzzy +msgid "Keep the last backup for each year for" +msgstr "Задржи све снимке за последњи" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "све године." + +#: qt/manageprofiles/tab_remove_retention.py:389 +#, fuzzy +msgid "… the free space is less than" +msgstr "Ако је слободан простор мањи од:" + +#: qt/manageprofiles/tab_remove_retention.py:394 +#, fuzzy +msgid "… the free inodes are less than" +msgstr "Ако је број слободних inode-ова мањи од:" + +#: qt/manageprofiles/tab_remove_retention.py:403 +#, fuzzy +msgid "Remove oldest backup if …" +msgstr "Уклањање старог снимка" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." msgstr "" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Пречице" + +#: qt/placeswidget.py:96 +msgid "Places" msgstr "" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/placeswidget.py:97 +msgid "File System" msgstr "" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Директоријуми резервне копије" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Профил: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, fuzzy, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Профил: {profile_name}" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Прикажи последњи извештај" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Покрени {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Радим…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" msgstr "" -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Увези конфигурацију" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" msgstr "" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Увези" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +#, fuzzy +msgid "Searching…" +msgstr "Радим…" + +#: qt/restoreconfigdialog.py:211 +#, fuzzy, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Изаберите директоријум снимка из којег треба да се увезе конфигурациони " +"фајл. Путања може изгледати овако: {samplePath}" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Ако се директоријум налази на спољном или удаљеном диску, мора бити ручно " +"монтиран пре тога." -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" msgstr "" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Diff opcije" +#: qt/restoreconfigdialog.py:256 +#, fuzzy +msgid "Show hidden directories" +msgstr "Прикажи скривене фајлове" -#: ../../qt/snapshotsdialog.py:60 +#: qt/restoreconfigdialog.py:258 +#, fuzzy +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Укључи фајлове и директоријуме" + +#: qt/restoreconfigdialog.py:305 +#, fuzzy +msgid "No config found in this directory" +msgstr "Креирање фајла није успело у овом директоријуму:" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "" + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Прикажи целокупни дневник" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Одбројавање до гашења" + +#: qt/shutdowndlg.py:29 +#, fuzzy +msgid "The backup has finished." +msgstr "Искључи систем након што снимак буде завршен." + +#: qt/shutdowndlg.py:36 +#, fuzzy +msgid "Cancel Shutdown" +msgstr "Искључивање" + +#: qt/shutdowndlg.py:37 +#, fuzzy +msgid "Shutdown Now" +msgstr "Искључивање" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/snapshotsdialog.py:61 +#, fuzzy +msgid "Options about comparing backups" +msgstr "Опције о упоређивању снимака" + +#: qt/snapshotsdialog.py:68 msgid "Command:" -msgstr "Naredba:" +msgstr "Команда:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" -msgstr "Parametri:" +msgstr "Параметри:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "Koristi %1 i %2 kao parametre putanje" +msgstr "Користи %1 и %2 као параметре за путање" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Молим поставите команду за тражење разлика или притисните Откажи." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Izlistaj samo različite snimke" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Команда \"{cmd}\" није пронађена на овом систему. Молимо пробајте нешто " +"друго или притисните Откажи." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Параметри нису постављени за команду за тражење разлика. Користиће се " +"подразумевана вредност \"{params}\"." -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +#, fuzzy +msgid "Backups" +msgstr "&Резервна копија" + +#: qt/snapshotsdialog.py:150 +#, fuzzy +msgid "Differing backups only" +msgstr "Само различити снимци" + +#: qt/snapshotsdialog.py:159 +#, fuzzy +msgid "List only backups that are equal to:" +msgstr "Излистај само снимке једнаке следећим:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "Detaljna provera (tačnija, ali spora)" +msgstr "Дубока провера (прецизнија али спора)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Избриши" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Одабери Све" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Упореди" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" -msgstr "Idi na" +msgstr "Иди на" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Nije moguće uporediti snimak sa samim sobom" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Опције" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Naredba nije pronađena: %s" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Нису могуће упоређивање резервне копије са самом собом, јер би упоређивање " +"било редундантно." -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:470 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Да ли стварно желите да избришете {file} у снимку {snapshot_id}?" + +#: qt/snapshotsdialog.py:475 +#, fuzzy, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Да ли стварно желите да избришете {file} у {count} снимака?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "УПОЗОРЕЊЕ: Ово се не може опозвати." + +#: qt/snapshotsdialog.py:495 +#, fuzzy, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Изостави {path} из будућих снимака?" + +#: qt/statusbar.py:85 +#, fuzzy +msgid "Root mode" +msgstr "Корен (root)" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:64 +msgid "Today" +msgstr "Данас" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Јуче" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Ове недеље" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Претходна седмица" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Задња провера {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#, fuzzy +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Ово НИЈЕ снимак, већ ужив приказ ваших локалних фајлова" diff --git a/common/po/sr_Latn.po b/common/po/sr_Latn.po new file mode 100644 index 000000000..d38c3af25 --- /dev/null +++ b/common/po/sr_Latn.po @@ -0,0 +1,2663 @@ +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Mališa "Maki711" Jevremović +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. +msgid "" +msgstr "" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-04-10 10:35+0000\n" +"Last-Translator: buhtz \n" +"Language-Team: Serbian (Latin script) \n" +"Language: sr_Latn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 5.10.2\n" + +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" + +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Upozorenje" + +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Glavni profil" + +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Lokalno (EncFS šifrovano)" + +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS šifrovano)" + +#: common/config.py:237 +msgid "Local" +msgstr "Lokalno" + +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Lokalno šifrovano" + +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Enkripcija" + +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" + +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH privatni ključ" + +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil: \"{name}\"" + +#: common/config.py:301 +#, fuzzy +msgid "Backup directory is not valid." +msgstr "Direktorijum za snimak nije važeći." + +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Najmanje jedan direktorijum za rezerne kopije mora biti izabran." + +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "Direktorijum: {path}" + +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "" +"Ovaj direktorijum ne može biti izabran za rezervne kopije pošto je deo " +"ciljnog direktorijuma za kopije." + +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" + +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" + +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Udev raspored ne radi sa režimom {mode}" + +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Neuspešno čuvanje novog crontab-a." + +#: common/config.py:1577 +#, fuzzy +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron nije pokrenut, uprkos tome što je crontab komanda dostupna. Zakazana " +"rezervna kopija neće biti pokrenuta. Cron je možda instaliran ali nije " +"omogućen. Isprobajte komandu \"systemctl enable cron\" ili kontaktirajte " +"podršku za GNU/Linux distribuciju." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Neuspešno čuvanje konfiguracije" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Neuspešno učitavanje konfiguracije" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Profil \"{name}\" već postoji." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Poslednji profil se ne može ukloniti." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Nije moguće montirati '{command}'" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Podešavanja za enkriptovani direktorijum nisu pronađena." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Napraviti novi enkriptovani direktorijum?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Otkazati" + +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "Molim vas unesite lozinku za \"{user}\"." + +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "Lozinka se ne poklapa." + +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Napravi snimak" + +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Nije moguće montirati '{command}'" + +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Nije moguće demontirati {mountprocess} sa {mountpoint}." + +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"{command} nije pronađen. Molimo da ga instalirate (npr. sa " +"\"{installcommand}\")" + +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Tačka montiranja {mntpoint} nije prazna." + +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Unesi šifru za {mode} profil \"{profile}\":" + +#: common/schedule.py:238 +#, fuzzy, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Neuspešno instaliranje Udev pravila za profil {profile_id}. DBus servis " +"'{dbus_interface}' nije dostupan" + +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Nije moguće naći UUID za {path}" + +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "NEUSPEŠNO" + +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Povrati dozvole" + +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Gotovo" + +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" + +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Odlaganje sigurnosne kopije dok je uređaj na bateriji" + +#: common/snapshots.py:931 qt/app.py:377 +#, fuzzy +msgid "Can't find backup directory." +msgstr "Ne mogu da pronađem direktorijum snimaka." + +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "Ako je na prenosivom disku, priključite ga." + +#: common/snapshots.py:938 +#, fuzzy, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Čeka se %s sekunda." +msgstr[1] "Čeka se %s sekunde." +msgstr[2] "Čeka se %s sekundi." + +#: common/snapshots.py:1005 +#, fuzzy, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Neuspešno čuvanje snimka {snapshot_id}." + +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Molim vas budite strpljivi. Završavanje u toku…" + +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Neuspešno kreiranje direktorijuma." + +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Čuvanje konfiguracionog fajla…" + +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Čuvanje dozvola…" + +#: common/snapshots.py:1377 +#, fuzzy, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "Pronađen preostali snimak {snapshot_id} koji se može nastaviti." + +#: common/snapshots.py:1401 +#, fuzzy, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "" +"Uklanjanje preostalog direktorijuma {snapshot_id} iz poslednjeg pokretanja" + +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Neuspešno uklanjanje direktorijuma" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "" + +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Uspelo" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Delimičan prenos zbog greške" + +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "" +"Delimičan prenos zbog iščezlih izvornih fajlova (pogledajte 'man rsync')" + +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' se završio sa izlaznim kodom {exit_code}" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Pogledaj 'man rsync' za više detalja" + +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "" +"Negativni rsync izlazni kodovi su signalni brojevi, pogledajte 'kill -l' i " +"'man kill'" + +#: common/snapshots.py:1566 +#, fuzzy +msgid "Nothing changed, no new backup necessary" +msgstr "Nikakve promene, nepotreban novi snimak" + +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Neuspešno preimenovanje {new_path} u {path}." + +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "Primenite pravila za uklanjanje starih snimaka" + +#: common/snapshots.py:2031 +#, fuzzy +msgid "Applying retention policy" +msgstr "Primenite smernice čuvanja" + +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Pokušavam da zadržim minimalno slobodnog prostora" + +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Pokušavam da zadržim minimalno {perc} slobodnih inode-ova" + +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Sada" + +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Nemoguće montirati {sshfs}" + +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent nije pronađen. Molim vas da se uverite da je instaliran." + +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "" +"Nije moguće otključati ssh privatni ključ. Pogrešna lozinka ili lozinka nije" +" dostupna za cron." + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Udaljena putanja postoji, ali nije direktorijum." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Udaljena putanja nije dostupan za pisanje." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Udaljena putanja nije izvršna." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Nije moguće kreirati udaljenu putanju." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Udaljena mašina {host} ne podržava {command}" + +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Provera komandi na mašini {host} vratila je nepoznatu grešku" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Udaljena mašina {host} ne podržava tvrde veze" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Kopiraj javni ssh-ključ \"{pubkey}\" na udaljenoj mašini \"{host}\"." + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Molim vas unesite lozinku za \"{user}\"." + +#: common/tools.py:382 +#, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." +msgstr "" +"Ciljni sistem datoteka za {path} je formatiran u NTFS, koji ima poznate " +"nekompatibilnost sa sistemima datoteka sličnim Unix-u." + +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "Putanja {path} nije važeći direktorijum." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Kreiranje sledećeg direktorijuma je bilo neuspešno:" + +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Pristup za pisanje je možda ograničen." + +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." +msgstr "" +"Ciljni sistema datoteka za {path} je formatiran sa FAT koji ne podržava " +"čvrste veze. Molim Vas da koristite izvorne sistemske datoteke GNU/Linux-a." + +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." +msgstr "" +"Ciljni sistema datoteke za {path} je deljeno mesto preko SMB. Molim Vas da " +"potvrdite da udaljeni SMB server podržava symlinks ili uključite {copyLinks}" +" u {expertOptions}." + +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Kopiraj linkove (prati simbolične linkove)" + +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Napredne Postavke" + +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." +msgstr "" +"Ciljni sistema datoteka za {path} je deljeno mesto preko SSHFS koji ne " +"podržava čvrste veze. Molimo vas da koristite 'SSH' umesto toga." + +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Kreiranje datoteke u ovom direktorijumu nije uspelo:" + +#: qt/aboutdlg.py:50 +#, fuzzy +msgid "About Back In Time" +msgstr "Back In &Time" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "" + +#: qt/aboutdlg.py:92 +#, fuzzy +msgid "Authors:" +msgstr "Autori" + +#: qt/aboutdlg.py:96 +#, fuzzy +msgid "Translators:" +msgstr "Prevodi" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Mališa \"Maki711\" Jevremović" + +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "" + +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "" + +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Web stranica projekta" + +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Uputstvo za upotrebu" + +#: qt/aboutdlg.py:247 qt/app.py:546 +#, fuzzy +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "" +"Otvorite uputstvo za upotrebu u pretraživaču (lokalno, ako je dostupno - " +"inače onlajn)" + +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "" + +#: qt/app.py:332 +#, fuzzy, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." +msgstr "" +"Izgleda da se {app_name} pokreće prvi put pošto konfiguracija nije " +"pronađena." + +#: qt/app.py:337 +#, fuzzy +msgid "" +"Import an existing configuration from a backup location or another computer?" +msgstr "" +"Uvezite postojeću konfiguraciju (iz direktorijuma sa rezervnim kopijama ili " +"sa drugog računara)?" + +#: qt/app.py:379 +msgid "Then press OK." +msgstr "" + +#: qt/app.py:483 +msgid "Create a backup" +msgstr "" + +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Koristi vreme i veličinu za detekciju promene datoteka." + +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "Napravi snimak (checksum režim)" + +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Koristi checksum-ove za detekciju promene datoteka." + +#: qt/app.py:492 qt/qtsystrayicon.py:125 +#, fuzzy +msgid "Pause backup process" +msgstr "Pauziraj proces snimanja" + +#: qt/app.py:496 qt/qtsystrayicon.py:130 +#, fuzzy +msgid "Resume backup process" +msgstr "Nastavi proces snimanja" + +#: qt/app.py:500 qt/qtsystrayicon.py:135 +#, fuzzy +msgid "Stop backup process" +msgstr "Zaustavi proces snimanja" + +#: qt/app.py:504 +#, fuzzy +msgid "Refresh backup list" +msgstr "Osveži listu snimaka" + +#: qt/app.py:508 +msgid "Name backup" +msgstr "" + +#: qt/app.py:512 +#, fuzzy +msgid "Remove backup" +msgstr "Ukloni snimak" + +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "Prikaži poslednji izveštaj" + +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "Najmanje jedan direktorijum za rezerne kopije mora biti izabran." + +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" +msgstr "Prikaži poslednji izveštaj" + +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "" + +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Upravljaj profilima…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Promeni user-callback" + +#: qt/app.py:532 +msgid "Shutdown" +msgstr "Gašenje" + +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." +msgstr "Ugasi sistem nakon završetka snimka." + +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Podesi jezik…" + +#: qt/app.py:540 +msgid "Exit" +msgstr "Izlaz" + +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "Man stranica: Back In Time" + +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "Prikaži man stranicu o Back In Time (backintime)" + +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "Man stranica: Konfiguracioni fajl profila" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" +"Prikaži man stranicu o konfiguracionalnom fajlu za profile (backintime-" +"config)" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "Otvori Back In Time web stranicu u pretraživaču" + +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "Izveštaj promena" + +#: qt/app.py:570 +#, fuzzy +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" +"Otvorite uputstvo za upotrebu u pretraživaču (lokalno, ako je dostupno - " +"inače onlajn)" + +#: qt/app.py:573 +msgid "FAQ" +msgstr "Česta pitanja (FAQ)" + +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Otvorite Često postavljana pitanja (FAQ) u pretraživaču" + +#: qt/app.py:577 +msgid "Ask a question" +msgstr "Postavi pitanje" + +#: qt/app.py:581 +msgid "Report a bug" +msgstr "Prijavi grešku" + +#: qt/app.py:584 +msgid "Translation" +msgstr "Prevod" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Ponovo prikaži poruku o učestvovanju u prevodu." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Prevod enkripcije (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Ponovo prikaži poruku o uklanjanju EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "O aplicaciji" + +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Povrati" + +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "" +"Povrati odabrane datoteke ili direktorijume na njihovu prvobitno odredište." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Povrati na …" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "Povrati odabrane datoteke ili direktorijume na novo odredište." + +#: qt/app.py:615 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "" +"Povrati trenutno prikazan direktorijum i sav njegov sadržaj na prvobitno " +"odredište." + +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "" +"Povrati trenutno prikazan direktorijum i sav njegov sadržaj na novo " +"odredište." + +#: qt/app.py:624 +msgid "Up" +msgstr "Gore" + +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Prikaži skrivene datoteke" + +#: qt/app.py:630 +#, fuzzy +msgid "Compare backups…" +msgstr "Uporedi snimke…" + +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Kandidat za izdavanje" + +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Ponovo prikaži poruku o kandidatu za izdavanje." + +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" + +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Rezervna kopija" + +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Povratak" + +#: qt/app.py:723 +msgid "&Help" +msgstr "P&omoć" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "Samo ikonice" + +#: qt/app.py:811 +msgid "Text only" +msgstr "Samo tekst" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Tekst ispod ikonica" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Tekst pored ikonica" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." +msgstr "" +"Ovaj direktorijum ne postoji\n" +"u aktuelnom snimku." + +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." +msgstr "" +"Ovaj direktorijum ne postoji\n" +"u aktuelnom snimku." + +#: qt/app.py:995 +#, fuzzy +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." +msgstr "" +"Ako zatvorite ovaj prozor Back In Time neće moći da ugasi vaš sistem kada se" +" snimak završi." + +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "" + +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Završeno, sigurnosna kopija nike potrebna" + +#: qt/app.py:1260 +msgid "Working:" +msgstr "U obradi:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Obrada" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Greška" + +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "Poslato:" + +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "Brzina:" + +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "ETA:" + +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "&Rezervna kopija" + +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Povrati {path}" + +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Povrati {path} u …" + +# ignore-placeholder-compare +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Zdravo\n" +"Koristili ste Back In Time na {language} jeziku već par puta.\n" +"Prevod vaše instalirane verzije Back In Time-a u {language} je {perc} gotov. Bez obzira na vaše tehničko znanje, sami možete doprineti prevodu, a time i Back In Time-u.\n" +"Molimo vas da posetite {translation_platform_url} ako želite da doprinesete. Za dalju pomoć i pitanja, posetite {back_in_time_project_website}\n" +"Izvinjavamo se zbog ometanja. Ova poruka se više neće pojaviti, ali je dostupna u bilo kom trenutku iz menija Pomoć\n" +"Vaš Back In Time tim" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "platforma za prevod" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Veb stranica" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Vaš prevod" + +#: qt/app.py:1709 +#, fuzzy, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "U Fediverse na Mastodon-u: {link_and_label}" + +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "Dopisna lista {link_and_label}" + +#: qt/app.py:1718 +#, fuzzy, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "Dopisna lista {link_and_label}" + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "{link_and_label} na web stranici projekta." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "Otvorite problem" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Alternativno, možete koristiti drugi kanal po svom izboru." + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Ova verzija Back In Time je kandidat za izdanje i prvenstveno je namenjena za testiranje stabilnosti u pripremi za sledeće zvanično izdanje.\n" +"Korisnički podaci ili telemetrija se ne prikupljaju. Međutim, tim Back In Time je veoma zainteresovan da zna da li će kandidat za izdanje biti korišćen i da li je vredno nastaviti da nudi takve verzije pre izdanja.\n" +"Stoga, tim ljubazno traži kratku povratnu informaciju o tome da li ste testirali ovu verziju, čak i ako niste naišli na probleme. Čak i kratko testiranje od samo nekoliko minuta bi nam mnogo pomoglo.\n" +"Dostupne su sledeće opcije kontakta:\n" +"{contact_list}\n" +"U ovoj verziji, ova poruka se više neće prikazivati, ali joj se može pristupiti u bilo kom trenutku preko menija pomoći (Help).\n" +"Hvala vam na podršci i što ste nam pomogli da poboljšamo Back In Time!\n" +"Vaš Back In Time tim" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." +msgstr "" + +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "" + +#: qt/app.py:1875 +#, fuzzy, python-brace-format +msgid "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." +msgstr "" +"{BOLD}Upozorenje{BOLDEND}: Brisanje datoteka u izvornom sistemu datoteka " +"može uništiti vaš ceo sistem." + +#: qt/app.py:2107 +#, fuzzy +msgid "Backup name" +msgstr "&Rezervna kopija" + +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "" +"Podešavanja jezika stupaju na snagu tek nakon ponovnog pokretanja Back In " +"Time-a." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "" + +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" +msgstr "" + +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "Zadrži imenovane snimke." + +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" +msgstr "" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Pitanje" + +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." +msgstr "" +"Napravite rezervne kopije sa pratećim {suffix}\n" +"pre pisanja preko ili uklanjanja lokalnih elemenata." + +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, fuzzy, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" +msgstr "" +"Novije verzije datoteka će se preimenovati sa pretećim {suffix} pre " +"vraćanja. Ako vam više nisu potrebne, možete ih ukloniti pomoću komande:" + +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Povratite samo elemente koji nepostoje ili\n" +"su noviji od onih u odredištu.\n" +"Ovo koristi \"rsync --update\"." + +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Ukloni novije elemente u originalnom direktorijumu." + +#: qt/confirmrestoredialog.py:135 +#, fuzzy +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Povratite odabrane datoteke ili direktorijume na originalno odredište i " +"izbrišite datoteke ili direktorijume koji se ne nalaze u snimku. Budite " +"izuzetno oprezni jer će ovo obrisati datoteke i direktorijume koji su bili " +"isključeni tokom pravljenja snimka." + +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"Da li zaista želite da povratite ovaj element u novi direktorijum {path}?" +msgstr[1] "" +"Da li zaista želite da vratite ove elemente u novi direktorijum {path}?" +msgstr[2] "" +"Da li zaista želite da vratite ove elemente u novi direktorijum {path}?" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Da li zaista želite da vratite ovaj element?" +msgstr[1] "Da li zaista želite da vratite ove elemente?" +msgstr[2] "Da li zaista želite da vratite ove elemente?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" + +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Inkludiraj datoteke i direktorijume" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Dodaj u listu inkluzija" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Dodaj u listu ekskluzija" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Podesite jezik" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Prevedeno: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Standardne sistemske opcije" + +#: qt/languagedialog.py:142 +#, fuzzy +msgid "Use operating system's language." +msgstr "Koristi jezik operativnog sistema." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "Prikaz zadnjeg protokola" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Prikaz zadnjeg protokola" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "Profil:" + +#: qt/logviewdialog.py:86 +#, fuzzy +msgid "Backups:" +msgstr "&Rezervna kopija" + +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "Filter:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Greška, [I] Informacija, [C] Izmena" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "dekodiraj putanju" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "Sve" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Izneme" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Greške" + +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Informacija" +msgstr[1] "Informacije" +msgstr[2] "Informacija" + +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "neuspešni rsync prenosi (eksperimentalno)" + +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Upravljaj profilima" + +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Uredi" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Dodaj" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Ukloni" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "Opšt&e" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Inkludirano" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "I&zostavi" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Uklanjanje & Vremensko čuvanje" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Opcije" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "&Napredne opcije" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Povrati konfiguraciju" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Novi profil" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Preimenuj profil" + +#: qt/manageprofiles/__init__.py:215 +#, fuzzy, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Da li ste sigurni da želite da uklonite profil \"{name}\" ?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "Pauziraj proces snimanja" + +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "Izostavi fajl" + +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Direktorijumi za rezervne kopije" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "Neuspešno uklanjanje direktorijuma" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Prikaži skrivene datoteke" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Zaustavi proces snimanja" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Izostavi direktorijum" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "standardno" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" +msgstr "Plan rada" + +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" +msgstr "Dan:" + +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" +msgstr "Radni dan:" + +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Vreme:" + +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" +msgstr "Sati:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "posle punog sata" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Minuti:" + +#: qt/manageprofiles/schedulewidget.py:93 +#, fuzzy +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Pokreni Back In Time čim se disk poveže (samo jednom svakih X dana). Od vas " +"će biti traženo da unesete svoju sudo lozinku." + +#: qt/manageprofiles/schedulewidget.py:98 +msgid "" +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." +msgstr "" +"Pokretanje Back In Time uzastopno. Ovo je korisno ako se kompjuter ne " +"koristi često." + +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" +msgstr "Svakih:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Omogućite evidentiranje poruka o greškama" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "Zapiši poruke debug nivoa u sistemski protokol pomoću \"--debug\"." + +#: qt/manageprofiles/schedulewidget.py:120 +msgid "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "" +"Oprez: ovo koristite samo privremeno za dijagnostiku, jer generiše veliku " +"količinu informacija." + +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Onemogućeno" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Pri svakom pokretanju računara/restartu" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Svakog {n} minuta" +msgstr[1] "Svakih {n} minuta" +msgstr[2] "Svakih {n} minuta" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Svakog {n} sata" +msgstr[1] "Svakih {n} sata" +msgstr[2] "Svakih {n} sati" + +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Prilagođeni sati" + +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Svakog dana" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Ponavljanje (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Kada se disk poveže (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Svake nedelje" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Svakog meseca" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Svake godine" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Sat(i)" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Dan(a)" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Nedelja/(e)" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Mesec(i)" + +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." +msgstr "" +"Prilagođeni sati mogu biti samo lista sati razdvojena zarezima (npr. " +"8,12,18,23) ili */3 za periodične sigurnosne kopije svakih 3 sata." + +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "Äldre än:" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Välj en befintlig privat nyckel-fil från annan plats." -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "Om ledigt utrymme är mindre än:" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "Om lediga inoder är mindre än:" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Skapa en ny SSH-nykel utan lösenfras." -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "Kör i bakgrunden på fjärrvärden." +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Fullständig genväg för säkerhetskopian: {path}" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "Behåll alla ögonblicksbilder för de senaste" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Privat nyckel:" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "dag(ar)" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Använd systemets SSH-konfiguration" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "Behåll en ögonblicksbild per dag de senaste" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "" +"Lämnar nyckel filen som ej vald. SSH anslutningarna kommer förlita sig på " +"systemets befintliga klientkonfiguration (t.ex., ~/.ssh/config)." -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "Behåll en ögonblicksbild per vecka för de senaste" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Proxy-server för SSH" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "vecka/veckor" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Värd:" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "Behåll en ögonblicksbild per månad de senaste" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Port:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "månad(er)" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Användare:" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "Behåll en ögonblicksbild per år för alla år" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." +msgstr "" +"Anslut till målvärden via den här proxyn (även känd som en hoppvärd). Se " +"\"-J\" i kommandodokumentationen för \"ssh\", eller \"ProxyJump\" i manualen" +" för \"ssh_config\", för detaljer." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Ta inte bort namngivna ögonblicksbilder" +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Info{ENDBOLD}: I \"SSH-krypterat\" läge är endast enkla eller dubbla " +"asterisker funktionella (t.ex. {example2}). Andra typer av jokertecken och " +"mönster kommer att ignoreras (t.ex. {example1}) Filnamn är oförutsägbara i " +"detta läge på grund av kryptering med EncFS." -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Inställningar" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Exkludera mönster, filer eller kataloger" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Aktivera aviseringar" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Lägg till mönster" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Inaktivera ögonblicksbilder vid batteridrift" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Lägg till filer" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Strömstatus inte tillgängligt från system" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Lägg till mappar" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Förslag" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "Välj bland vanliga objekt att lägga till i undantagslistan." + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Exkludera filer större än:" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "Kör bara en ögonblicksbild i taget" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Exkludera filer som är större än värdet i {size_unit}." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" -"Andra ögonblicksbilder blockeras tills den aktuella ögonblicksbilden är " -"färdig.\n" -"Detta är en global inställning. Så det kommer att påverka alla profiler för " -"denna användaren.\n" -"Men du behöver aktivera detta för alla andra användare också." +"Med 'Fullständigt rsync-läge' inaktiverat så kommer den här inställningen " +"enbart att påverka nya filer eftersom att rsync behandlar det här som ett " +"överföringsalternativ istället för som ett undantagsalternativ. Följaktligen" +" kommer stora filer som redan har säkerhetskopierats att finnas kvar i " +"säkerhetskopiorna även om de ändras." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "Säkerhetskopiering ersatte filer vid återställning" - -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Fortsätt vid fel (behåll icke kompletta ögonblicksbilder)" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Exkludera mönster" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "Ta en ny ögonblicksbild oavsett om det fanns ändringar eller inte." +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Ange ett exkluderings mönster:" -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Loggnivå:" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "För hjälp, se rsync manual-sida-avsnittet {link}." -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Inga" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Öppna rsync manualsida" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Ändringar & fel" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Exkludera filer" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Ändra dessa inställningar endast om du vet vad du gör!" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Exkludera mappar" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "Kör \"nice\":" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "" +"Avstängt eftersom att det här mönstret inte är funktionellt i läget " +"'Krypterad SSH'." -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "" +"Dessa alternativ är för avancerade konfigurationer. Ändra endast om du är " +"fullt medveten om deras konsekvenser." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Kör 'rsync' med '{cmd}':" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" msgstr "som cronjobb" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" msgstr "på fjärrvärd" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "Kör \"ionice\":" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "när en manuell säkerhetskopiering görs" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "vid tagning av en manuell ögonblicksbild" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Vänligen installera 'nocache' för att aktivera detta alternativ." -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "Kör \"rsync\" med \"nocache\":" - -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" msgstr "på lokal maskin" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Omdirigera stdout till /dev/null i cronjobb." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "" +"Cron kommer automatiskt att skicka e-post med bifogad utmatning av cron-jobb" +" om en MTA är installerad." + +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Omdirigera stderr till /dev/null i cronjobb." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "Begränsa rsync-bandbreddsanvändning: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "" +"Cron kommer automatiskt att skicka ett e-postmeddelande med bifogade fel i " +"cron-jobb om en MTA är installerad." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr " KB/sek" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/s" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Bandbreddsbegränsning för rsync:" + +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "Behåll ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Behåll utökade attribut (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "Kopiera osäkra länkar (fungerar endast med absoluta länkar)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Begränsa till ett filsystem" + +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Alternativ måste skrivas inom citationstecken t.ex. {example}." -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" msgstr "Klistra in ytterligare alternativ till rsync" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Prefix som ska köras före varje kommando på fjärrvärden." + +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" -"Alternativ måste citeras t.ex. --exclude-from=\"/sökväg/till/min exkludera " -"fil\"." +"Variabler måste avslutas med \\$FOO. Detta rör inte rsync. Så för att lägga " +"till ett prefix för rsync använd \"{example_value}\" med " +"{rsync_options_value}." -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "Lägg till prefix till SSH-kommandon" - -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" -msgstr "" -"Prefix att köra före varje kommando på fjärrvärd.\n" -"Variabler behöver undvikas med \\$FOO.\n" -"Detta berör inte rsync. Så för att lägga till ett prefix\n" -"för rsync använd \"%(cbRsyncOptions)s\" med\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" - -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 +#: qt/manageprofiles/tab_expert_options.py:293 msgid "default" msgstr "standard" -#: ../../qt/settingsdialog.py:870 +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "Lägg till prefix till SSH-kommandon" + +#: qt/manageprofiles/tab_expert_options.py:308 msgid "Check if remote host is online" msgstr "Kontrollera om fjärrvärd är uppkopplad" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" -"Varning: om den är inaktiverad och fjärrvärden\n" -"inte är tillgänglig, kan detta leda till några\n" -"konstiga fel." +"Varning: Om det är inaktiverat och fjärrvärden inte är tillgänglig kan detta" +" leda till konstiga fel." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "Kontrollera om fjärrvärd stöder alla nödvändiga kommandon" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Kontrollera om fjärrvärden stöder alla nödvändiga kommandon." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" -"Varning: om inaktiverad och fjärrvärden\n" -"inte stöder alla nödvändiga kommandon,\n" -"kan detta leda till några konstiga fel." +"Varning: Om det är inaktiverad och fjärrvärden inte stöder alla nödvändiga " +"kommandon kan detta leda till konstiga fel." -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "Återställ Config" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(standard: {})" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "Redigera user-callback" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "inaktiverad" -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" -"Full systemsäkerhetskopiering kan bara skapa en ögonblicksbild som ska " -"återställas till samma fysiska disk(ar) med samma diskpartitionering som " -"från källan; återställa till nya fysiska skivor eller samma skivor med olika " -"partitionering ger ett potentiellt brutet och oanvändbart system.\n" -"\n" -"Full systemsäkerhetskopiering åsidosätter några inställningar som kan ha " -"anpassats. Fortsätta?" - -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Ny profil" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "aktiverad" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Byt namn på profil" +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Läge:" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Är du säker på att du vill ta bort profilen \"%s\" ?" +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Var säkerhetskopiorna ska sparas" -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH-inställningar" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Sökväg:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Ny profil:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Lösenord" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Spara lösenord till nyckelring" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "" +"Spara lösenordet i en cache för Cron (säkerhetsproblem: root kan läsa " +"lösenordet)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Avancerat" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Fullständig genväg för säkerhetskopian:" + +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "" +"Schemaläggning är inaktiverad eftersom ingen cron-installation hittades. " +"Installera cron för att aktivera schemalagda säkerhetskopior." + +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Sökväg för säkerhetskopia får inte vara tom." + +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Krypteringslösenordet får inte vara tomt." + +#: qt/manageprofiles/tab_general.py:553 +msgid "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" +msgstr "" +"Ett fel inträffade vid försök att logga in till fjärrenheten. Följande " +"felmeddelande returnerades:" + +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" -"Anpassade timmar kan endast vara en kommaseparerad lista över timmar (t.ex. " -"8,12,18,23) eller */3 för periodiska säkerhetskopior var 3:e timme" +"För att aktivera lösenordsfri inloggning kan den publika SSH-nyckeln " +"kopieras till fjärrdatorn." -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Fortsätt med att kopiera SSH-nyckel?" + +#: qt/manageprofiles/tab_general.py:585 msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." +msgstr "" +"Den publika SSH nyckeln kunde inte kopieras. Detta kan bero på ett " +"anslutnings eller behörighetsfel." + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Autenticiteten för värd {host} kan inte fastställas." + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} nyckelfingeravtryck är:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" msgstr "" -"Du valde inte en privat nyckelfil för SSH.\n" -"Vill du skapa ett nytt lösenordsfritt offentligt/privat nyckelpar?" +"Vänligen bekräfta det här fingeravtrycket. Lägg till det i din " +"'known_hosts'-fil?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Verkligen ändra katalog för säkerhetskopiering?" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "Privat nyckelfil \"%(file)s\" finns inte." +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Den valda säkerhetskopia destinationen är inte tom." -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Den får inte vara tom för att använda kryptering." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Ogiltig fil: Inte en privat SSH-nyckel" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" -"Vill du kopiera din offentliga SSH-nyckel till\n" -"fjärrvärden för att aktivera lösenordslös inloggning?" +"Den valda filen ({path}) är en publik SSH nyckel. Vänligen välj den " +"motsvarande privata nyckeln istället (utan \".pub\")." -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" -"Autenticiteten för värd \"%(host)s\" kan inte fastställas.\n" -"\n" -"%(keytype)s nyckelfingeravtryck är:" +"Filen {path} finns redan. Kan inte skapa en ny SSH nyckel med det namnet." + +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Misslyckades med att skapa ny SSH-nyckel i {path}." -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Inkludera filer och mappar" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" -"Kontrollera detta fingeravtryck! Vill du lägga till det i din " -"\"known_hosts\"-fil?" +"\"{path}\" är en sym-länk. Det länkade målet kommer inte att " +"säkerhetskopieras förrns även det är inkluderat." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Exkludera mönster" +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Inkludera symlänkens mål istället?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Inkludera filer" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Exkludera fil" +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Inkludera mappar" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Exkludera mapp" +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Aktivera aviseringar" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Inkludera fil" +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Stäng av säkerhetskopieringar när batteriet används" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Strömstatus inte tillgängligt från system" + +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Kör enbart en säkerhetskopia i taget" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" -"\"%s\" är en symlink. Det länkade målet kommer inte att säkerhetskopieras " -"tills du inkluderar det också.\n" -"Vill du inkludera symlinks-målet istället?" +"De andra säkerhetskopieringarna kommer att blockeras tills den aktuella " +"säkerhetskopieringen är klar. Det här är ett globalt alternativ vilket " +"betyder att det kommer att drabba alla profiler för den här användaren. Det " +"måste hur som helst aktiveras för alla andra användare." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Inkludera mapp" +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Säkerhetskopiera ersatta filer vid återställning" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Är du säker på att du vill ändra mappen för ögonblicksbilder?" +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Fortsätt vid fel (behåll inte säkerhetskopior som inte är slutförda)" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "Misslyckades med att skapa ny SSH-nyckel i %(path)s" +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Använd checksummor för att detektera ändringar" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "aktiverad" +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Skapa en ny säkerhetskopia oavsett om det fanns ändringar eller inte." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "inaktiverad" +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Varna om det lediga lagringsutrymmet underskrider" + +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." +msgstr "" +"Visa en varning när ledigt utrymme på lagringsenheten för säkerhetskopians " +"destination är mindre än det specificerade värdet." + +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." +msgstr "" +"Om borttags- och behållandepolicyn är aktiverad och äldre säkerhetskopior " +"blir borttagna baserat på tillgängligt fritt utrymme, kan detta värde inte " +"vara lägre än värdet satt i den policyn." + +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Loggnivå:" + +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Inga" + +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "användarmanual" + +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "" +"Följande regler bearbetas uppifrån och ner. Senare regler åsidosätter " +"tidigare regler. Se {manual_link} för detaljer och exempel." + +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Öppna användarmanual i webbläsaren." + +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Behåll den mest senaste säkerhetskopian." + +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Den mest aktuella säkerhetskopian bevaras under alla omständigheter." + +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Det beteendet går inte att ändra på." + +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Behåll namngivna säkerhetskopior." + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." +msgstr "" +"Säkerhetskopian som har fått ett namn, utöver den vanliga tidsstämpeln, " +"kommer att behållas under alla omständigheter och kommer inte att tas bort." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "År" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Ta bort säkerhetskopior som är äldre än" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Hela dagar. Aktuell dag ignoreras." + +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "Kalenderveckor med måndag som första dag. Aktuell vecka ignoreras." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Tolvmånaders perioder. Nuvarande månad ignoreras." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Bevarandepolicy" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Kör i bakgrunden på fjärrvärden." + +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "" +"Bevaringspolicyn kommer att köras direkt på fjärrdatorn, inte lokalt. " +"Kommandona \"bash\", \"screen\" och \"flock\" måste vara installerade och " +"tillgängliga på fjärrdatorn." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Om det väljs kommer Back In Time först att testa fjärrmaskinen." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Dagarna räknas från och med idag." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Behåll alla säkerhetskopior för de senaste" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "dag(ar)." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Behåll den sista säkerhetskopian för varje dag till den sista" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "Återställ inställningar" - -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr " lägg till din användare till gruppen \"fuse\"" - -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" -"Navigera till ögonblicksbilden från vilken du vill återställa %(appName)ss " -"konfiguration. Sökvägen kan se ut som: \n" -"%(samplePath)s\n" -"\n" -"Om dina ögonblicksbilder finns på en fjärrdisk eller om de är krypterade " -"måste du manuellt montera dem först. Om du använder Mode SSH kan du också " -"behöva ställa in inloggningen för offentlig nyckel till fjärrvärden " -"%(addFuse)s.\n" -"Ta en titt på \"man backintime\"." - -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "Ingen konfiguration hittades" - -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "user-callback skript har ingen shebang (#!/bin/sh) rad." - -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "Shebang i user-callback skript är inte körbart." - -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Jämförelsealternativ" - -#: ../../qt/snapshotsdialog.py:60 +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "" +"Veckorna räknas från och med den aktuella löpveckan. En vecka börjar på " +"måndag." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Behåll den sista säkerhetskopian för varje vecka till den sista" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "vecka/veckor." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Månaderna räknas som kalendermånader från och med nuvarande månad." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Behåll den sista säkerhetskopian för varje månad till den sista" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "månad(er)." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Åren räknas som kalenderår från och med det nuvarande året." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Spara den sista säkerhetskopior för varje år för" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "alla år." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… det lediga utrymmet är mindre än" + +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… de lediga inoderna är mindre än" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Ta bort gamla säkerhetskopior om …" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "Autentisering krävs för att köra Back In Time som root." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "" +"Autentisering krävs för att ändra på schemalagda säkerhetskopieringar som " +"triggas av att externa lagringsenheter ansluts." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Genvägar" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Platser" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Filsystem" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Mappar för säkerhetskopior" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Profil: \"{profile_name}\"" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Profil: \"{profile_name}\" (av användare \"{desktop_user}\")" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Visa senaste logg" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Starta {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Arbetar…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "manualsida: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Importera konfiguration" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Ingen katalog har valts" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Importera" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Söker…" + +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "" +"Välj mappen för säkerhetskopian som konfigurationsfilen ska importeras " +"ifrån. Genvägen kan se ut så här: {samplePath}" + +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "" +"Om katalogen finns på en extern eller fjärrenhet måste den monteras manuellt" +" i förväg." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Skanna igen" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Visa dolda mappar" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Visa/göm dolda mappar (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Ingen config hittades i den här katalogen" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Sökning slutförd." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Visa fullständig logg" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Nedräkning till avstängning" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Säkerhetskopieringen har slutförts." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Avbryt avstängning" + +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Stäng av nu" + +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Systemet kommer att stängas av om {n} sekund." +msgstr[1] "Systemet kommer att stängas av om {n} sekunder." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Alternativ för att jämföra säkerhetskopior" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Kommando:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Parametrar:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "Använd %1 och %2 för sökvägsparametrar" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Lista endast olika ögonblicksbilder" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Vänligen ställ in ett diff-kommando eller tryck på Avbryt." + +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Kommandot \"{cmd}\" kan inte hittas i ditt system. Vänligen prova något " +"annat eller tryck på Avbryt." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "Lista endast lika ögonblicksbilder till: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "" +"Inga parametrar är inställda för diff-kommandot. Använder standardvärdet " +"\"{params}\"." -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Säkerhetskopior" + +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Endast avvikande säkerhetskopior" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Lista enbart säkerhetskopior som är lika med:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" msgstr "Djupkontroll (mer noggrann, men långsam)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "Ta bort" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" msgstr "Välj alla" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Jämförelse" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Jämför" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Gå till" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Du kan inte jämföra en ögonblicksbild med sig själv" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Alternativ" -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Kommando hittades inte: %s" +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "" +"Det är inte möjligt att jämföra en säkerhetskopia med sig själv eftersom att" +" jämförelsen skulle vara överflödig." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Verkligen ta bort {file_or_dir} i säkerhetskopian {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Vill du verkligen ta bort {file_or_dir} i {count} säkerhetskopior?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "VARNING: Det här kan inte ångras." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Exkludera {path} från framtida säkerhetskopior?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Root-läge" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" +msgstr "" +"Back In Time körs för närvarande med root behörigheter (full åtkomst till " +"systemet)" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Idag" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Igår" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Denna vecka" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Förra veckan" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -"Vill du verkligen ta bort \"%(file)s\" i ögonblicksbild \"%(snapshot_id)s?\n" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -"Vill du verkligen ta bort \"%(file)s\" i %(count)d ögonblicksbilder?\n" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "VARNING: Det här kan inte återkallas!" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Senaste kontroll {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." +msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "Exkludera \"%s\" från framtida ögonblicksbilder?" +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "" +#~ "Det här är INTE en säkerhetskopia utan en direktvisning av de lokala " +#~ "filerna." diff --git a/common/po/th.po b/common/po/th.po deleted file mode 100644 index c8e47e508..000000000 --- a/common/po/th.po +++ /dev/null @@ -1,1646 +0,0 @@ -# Thai translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. -# -msgid "" -msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2009-03-12 08:48+0000\n" -"Last-Translator: Thammaraj \n" -"Language-Team: Thai \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" - -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "" - -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "" - -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "" - -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "" - -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "ทุก 5 นาที" - -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "ทุก 10 นาที" - -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "" - -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "" - -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "" - -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "" - -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "" - -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "" - -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "" - -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "ทุกวัน" - -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "" - -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "" - -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "ทุกสัปดาห์" - -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "ทุกเดือน" - -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "วัน" - -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "สัปดาห์" - -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "ปี" - -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" - -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" - -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" - -#: ../../common/config.py:129 -msgid "Local" -msgstr "" - -#: ../../common/config.py:130 -msgid "SSH" -msgstr "" - -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "" - -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "" - -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "" - -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" - -#: ../../common/config.py:135 -msgid "Default" -msgstr "" - -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "" - -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "" - -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "" - -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "" - -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "" - -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "" - -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "" - -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "" - -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "" - -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "" - -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "" - -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "" - -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "" - -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "" - -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "" - -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "" - -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "" - -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "" - -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "" - -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" - -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" - -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" - -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" - -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "" - -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "" - -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" - -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" - -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" - -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" - -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" - -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" - -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" - -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" - -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" - -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" - -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" - -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "" - -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "" - -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" - -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" - -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" - -#: ../../common/mount.py:590 -#, python-format -msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" - -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" - -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" - -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" - -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" - -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "" - -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "ทำเสร็จ" - -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" -msgstr "" - -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "" - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "" -msgstr[1] "" - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "" - -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "" - -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "" - -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "" - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "" - -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" - -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" - -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "" - -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "" - -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" - -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" - -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "" - -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "" - -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "" - -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" - -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "" - -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "เดี๋ยวนี้" - -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" - -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" - -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" - -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" - -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" - -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:470 -#, python-format -msgid "" -"Remote path is not writable:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:472 -#, python-format -msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" - -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" - -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" - -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" - -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" - -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" - -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" - -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" - -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" - -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" - -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" - -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "" - -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "" - -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "" - -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "" - -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "" - -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "การตั้งค่า" - -#: ../../qt/app.py:142 -msgid "Shutdown" -msgstr "" - -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" - -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "ออก" - -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "ช่วยเหลือ" - -#: ../../qt/app.py:160 -msgid "Config File Help" -msgstr "" - -#: ../../qt/app.py:163 -msgid "Website" -msgstr "" - -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" -msgstr "" - -#: ../../qt/app.py:167 -msgid "FAQ" -msgstr "" - -#: ../../qt/app.py:169 -msgid "Ask a question" -msgstr "" - -#: ../../qt/app.py:171 -msgid "Report a bug" -msgstr "" - -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "เกี่ยวกับ" - -#: ../../qt/app.py:204 -msgid "Up" -msgstr "ขึ้น" - -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "แสดงแฟ้มที่ซ่อน" - -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "" - -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" - -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "" - -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" - -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" - -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" - -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" - -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "" - -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" - -#: ../../qt/app.py:271 -msgid "View" -msgstr "" - -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "" - -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" - -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" - -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" - -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" -msgstr "" - -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" -msgstr "" - -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" -msgstr "" - -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "" - -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "ทำเสร็จ, ไม่จำเป็นต้องสำรอง" - -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "" - -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" -msgstr "" - -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" -msgstr "" - -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" -msgstr "" - -#: ../../qt/app.py:794 -msgid "Global" -msgstr "" - -#: ../../qt/app.py:795 -msgid "Root" -msgstr "" - -#: ../../qt/app.py:796 -msgid "Home" -msgstr "" - -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "" - -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" - -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" - -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format -msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" - -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" - -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" - -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" - -#: ../../qt/app.py:1083 -#, python-format -msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" -msgstr "" - -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" - -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" - -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" - -#: ../../qt/app.py:1105 -msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" -msgstr "" - -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "" - -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "" - -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "" - -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" - -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "" - -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" - -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" - -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" - -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" - -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "" - -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "" - -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "" - -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "" - -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "" - -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "" - -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" - -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" - -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" - -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" - -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" - -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" - -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" - -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "" - -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "วันนี้" - -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "เมื่อวาน" - -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "สัดาห์นี้" - -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "สัปดาห์ที่แล้ว" - -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" - -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" - -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "" - -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "" - -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" - -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" - -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "" - -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "" - -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "" - -#: ../../qt/settingsdialog.py:129 -#, python-format -msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." -msgstr "" - -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "" - -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" - -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "" - -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "" - -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "" - -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "" - -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "" - -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" - -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "" - -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" - -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" - -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "" - -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" - -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" - -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" -msgstr "" - -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "" - -#: ../../qt/settingsdialog.py:307 -msgid "Schedule" -msgstr "" - -#: ../../qt/settingsdialog.py:317 -msgid "Day:" -msgstr "" - -#: ../../qt/settingsdialog.py:328 -msgid "Weekday:" -msgstr "" - -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "" - -#: ../../qt/settingsdialog.py:350 -msgid "Hours:" -msgstr "" - -#: ../../qt/settingsdialog.py:359 -msgid "" -"Run Back In Time repeatedly. This is useful if the computer is not running " -"regularly." -msgstr "" - -#: ../../qt/settingsdialog.py:364 -msgid "Every:" -msgstr "" - -#: ../../qt/settingsdialog.py:382 -msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" - -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "" - -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" - -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "" - -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "" - -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "" - -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" - -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" - -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" - -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" - -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" - -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" - -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "" - -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "" - -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "" - -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" - -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" - -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "" - -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "" - -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" - -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "" - -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "" - -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "" - -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "" - -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "" - -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "" - -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "" - -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "" - -#: ../../qt/settingsdialog.py:625 -msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" - -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" - -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "" - -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" - -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "" - -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "" - -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "" - -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "" - -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "" - -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" -msgstr "" - -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" -msgstr "" - -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" - -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" - -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" - -#: ../../qt/settingsdialog.py:714 -msgid "on local machine" -msgstr "" - -#: ../../qt/settingsdialog.py:721 -msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "" - -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" - -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "" - -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" - -#: ../../qt/settingsdialog.py:774 -msgid "Preserve ACL" -msgstr "" - -#: ../../qt/settingsdialog.py:787 -msgid "Preserve extended attributes (xattr)" -msgstr "" - -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" - -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" -msgstr "" - -#: ../../qt/settingsdialog.py:839 -msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." -msgstr "" - -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "" - -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" -msgstr "" - -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "" - -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" -msgstr "" - -#: ../../qt/settingsdialog.py:871 -msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." -msgstr "" - -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "" - -#: ../../qt/settingsdialog.py:875 -msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." -msgstr "" - -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "" - -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "" - -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" - -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "" - -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "" - -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "" - -#: ../../qt/settingsdialog.py:1201 -msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" -msgstr "" - -#: ../../qt/settingsdialog.py:1237 -msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" -msgstr "" - -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" - -#: ../../qt/settingsdialog.py:1360 -msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" -msgstr "" - -#: ../../qt/settingsdialog.py:1376 -#, python-format -msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" -msgstr "" - -#: ../../qt/settingsdialog.py:1384 -msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" -msgstr "" - -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "" - -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "" - -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "" - -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "" - -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format -msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" -msgstr "" - -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "" - -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "" - -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" - -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "" - -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" - -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "" - -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr "" - -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" - -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "" - -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "" - -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "" - -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "" - -#: ../../qt/snapshotsdialog.py:60 -msgid "Command:" -msgstr "" - -#: ../../qt/snapshotsdialog.py:64 -msgid "Parameters:" -msgstr "" - -#: ../../qt/snapshotsdialog.py:68 -msgid "Use %1 and %2 for path parameters" -msgstr "" - -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "" - -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "" - -#: ../../qt/snapshotsdialog.py:128 -msgid "Deep check (more accurate, but slow)" -msgstr "" - -#: ../../qt/snapshotsdialog.py:149 -msgid "Delete" -msgstr "" - -#: ../../qt/snapshotsdialog.py:153 -msgid "Select All" -msgstr "" - -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "" - -#: ../../qt/snapshotsdialog.py:177 -msgid "Go To" -msgstr "ไปที่" - -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "" - -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "" - -#: ../../qt/snapshotsdialog.py:361 -#, python-format -msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" -msgstr "" - -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" -msgstr "" - -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "" - -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" -msgstr "" diff --git a/common/po/tr.po b/common/po/tr.po index 52def8de0..d2efee9f9 100644 --- a/common/po/tr.po +++ b/common/po/tr.po @@ -1,1663 +1,2621 @@ -# Turkish translation for backintime -# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2009. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Onur Esat Karabacak +# SPDX-FileCopyrightText: © Taner Orak +# SPDX-FileCopyrightText: © Nuri Küçükler +# SPDX-FileCopyrightText: © Baris Ozyurt # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2017-01-11 12:24+0000\n" -"Last-Translator: Numan Demirdöğen \n" -"Language-Team: Turkish \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-02-02 07:08+0000\n" +"Last-Translator: barisozyurt \n" +"Language-Team: Turkish \n" +"Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" - -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" -msgstr "Ayar kaydedilemedi: %s" - -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "Ayar yüklenemedi: %s" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "\"%s\" profili zaten mevcut !" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" +"Bu projedeki bütün lisanslar {dir_link} dizininde konumlandırılmıştır. SPDX " +"metaverisini kullanarak dosya bazında lisans ve telif bilgisini çözümlemek " +"için şu linke başvurun: {readme_link}." -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "Son profili kaldıramazsınız !" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Uyarı" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "Etkisizleştirildi" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Ana profil" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "Her yeniden başlattığında" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Yerel (EncFS şifreli)" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "Her 5 dakikada bir" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (EncFS şifreli)" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "Her 10 dakikada bir" +#: common/config.py:237 +msgid "Local" +msgstr "Yerel" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "30 dakikada bir" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Yerel olarak şifrelenmiş" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "Saatte bir" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Şifreleme" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "2 saatte bir" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "4 saatte bir" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH özel anahtar" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "6 saatte bir" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Profil: \"{name}\"" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "12 saatte bir" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "Yedek dizini geçersiz." -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "Kullanıcı tanımlı saatlerde" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Yedek için en az bir klasör seçilmelidir." -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "Her gün" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "{path} dizini" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." msgstr "" +"Bu dizin yedek dizininin bir parçası olduğundan yedeğe dahil edilemez." -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "Disk bağlandığında (udev)" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" +"\"Boş alan daha azsa en eski yedeklemeyi kaldır\" ({val_one}) değeri, \"Boş " +"disk alanı altına düşerse uyar\" ({val_two}) eşiğinden küçük veya eşit " +"olmalıdır." -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "Her Hafta" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" +"Lütfen yedekleme kaldırma sınırının uyarı sınırından yüksek olmaması için " +"ayarları düzenleyin." -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "Her Ay" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Udev zamanlama kipi {mode} ile çalışmıyor" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "Günde" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Yeni crontab yazımı başarısız oldu." -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "Haftada" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Crontab komutu mevcut olmasına rağmen Cron çalışmıyor. Planlanan yedekleme " +"işleri çalışmayacak. Cron yüklü, ancak etkinleştirilmemiş olabilir. " +"\"systemctl enable cron\" ve \"systemctl start cron\" komutlarını deneyin " +"veya GNU Linux dağıtımınızın destek kanallarına başvurun." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Yapılandırma kaydedilemedi" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Yapılandırma yüklenemedi" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "\"{name}\" profili zaten var." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Son profil kaldırılamaz." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "'{command}' bağlanamıyor" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "Şifrelenmiş dizin için yapılandırma bulunamadı." + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "Yeni bir şifreli dizin oluşturulsun mu?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "İptal" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "Yılda" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "Lütfen onaylamak için EncFS şifresini tekrar girin." -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "Saatte" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFS parolaları eşleşmiyor." -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "Ayda" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Yedek al" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr " KARARSIZ!" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "\"{command}\" şifrelenmiş yolunu başlatamadı" -#: ../../common/config.py:129 -msgid "Local" -msgstr "Yerel" +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "{mountprocess} , {mountpoint} den ayrılamıyor." -#: ../../common/config.py:130 -msgid "SSH" -msgstr "SSH" +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"{command} bulunamadı. Lütfen (örneğin {installcommand} komutunu kullanarak) " +"yükleyin" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "Gizli SSH anahtarı" +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Bağlantı noktası {mntpoint} boş değil." -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "Şifrelenmiş yerel" +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Şifrenizi {mode} \"{profile}\" profili için giriniz :" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "Şifreleme" +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"{profile_id} profili için Udev kuralı yüklenemedi. '{dbus_interface}' DBus " +"Hizmeti kullanılabilir değildi." -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "Şifrelenmiş SSH" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "{path} için UUID bulunamadı" -#: ../../common/config.py:135 -msgid "Default" -msgstr "Varsayılan" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "BAŞARISIZ" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "AES128-CTR" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "İzinleri geri yükle" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "AES192-CTR" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Tamamlandı" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "AES256-CTR" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" +"Ekleme listesindeki aşağıdaki kayıtların yedekleme kaynağında karşılık gelen" +" bir dosyası veya dizini yoktur:" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "ARCFOUR256" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Pille çalışırken yedeklemeyi erteleme" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "ARCFOUR128" +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "Yedekleme klasörü bulunamadı." -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "AES128-CBC" +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "Eğer bu bir çıkarılabilir sürücüde ise, lütfen sisteme takın." -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "3DES-CBC" +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "{n} saniye bekleniyor." +msgstr[1] "{n} saniye bekleniyor." -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "Blowfish-CBC" +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "{snapshot_id} yedeği alınamadı." -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "Cast128-CBC" +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "Lütfen sabırlı olun. Tamamlanıyor…" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "AES192-CBC" +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Dizin oluşturulamıyor." -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "AES256-CBC" +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Yapılandırma dosyası kaydediliyor…" -#: ../../common/config.py:147 -msgid "ARCFOUR" -msgstr "ARCFOUR" +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "İzinler kaydediliyor…" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "Ana profil" +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "Devam edilebilecek tamamlanmamış {snapshot_id} yedeği bulundu." -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "Profil: \"%s\"" +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "" +"Son çalıştırmadan kalan tamamlanmamış {snapshot_id} dizini kaldırılıyor" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "Anlıkçekim klasörü geçerli değil !" +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "Dizin silinemiyor" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "Yedekleme için en az bir klasör seçmelisiniz !" +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "Yedek oluşturuluyor" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "Yedekleme klasörünü dahil edemezsiniz !" +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Başarılı" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "Yedekleme alt-klasörünü dahil edemezsiniz !" +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Hata sebebiyle kısmen aktarıldı" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s bir klasör değil !" +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "Kaybolan kaynak dosyalar nedeniyle kısmi aktarım (bkz. 'man rsync')" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "Sunucu/Kullanıcı/Profil ID boş bırakılmamalı" +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' sonlandı, çıkış kodu: {exit_code}" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"Yazılamıyor: %s\n" -"Yazma izniniz olduğuna emin misiniz ?" +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Daha fazla ayrıntı için bakınız; 'man rsync'" -#: ../../common/config.py:402 -#, python-format +#: common/snapshots.py:1545 msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" -"'%(path)s' için girilen hedef dosya sistemi sabit bağları desteklemeyen FAT " -"ile biçimlendirilmiş. Lütfen bir Linux dosya sistemi kullanın." +"Negatif rsync çıkış kodları sinyal numaralarıdır, bkz. 'kill -l' ve 'man " +"kill'" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" -"'%(path)s' için girilen hedef dosya sistemi bir paylaşımlı SMB dizini. " -"Lütfen SMB sunucunun sembolik bağları desteklediğini teyit edin ya da " -"'%(expertOptions)s' bölümünden '%(copyLinks)s' seçeneğini etkinleştirin." +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "Hiçbir değişiklik yok, yeni yedek gerekmiyor" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "Bağları kopyala (sembolik bağların hedef dosyaslarını kopyalar)" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "{new_path} yolu {path} olarak yeniden adlandırılamıyor." -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "Uzman Seçenekleri" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "Eski yedekleri kaldırmak için kurallar uygulanıyor" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" -"'%(path)s' için girilen hedef dosya sistemi bir paylaşımlı sshfs dizini. " -"sshfs sabit bağları desteklemiyor. Lütfen 'SSH' usulünü kullanın." +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "Saklama politikası kuralları uygulanıyor" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"crontab bulunamadı.\n" -"cron'un yüklenmiş olduğundan emin misiniz ?\n" -"Eğer değilseniz tüm otomatik yedeklemeleri etkisiz hale getirmeniz gerekir." +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "En az boş alan korunmaya çalışılıyor" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "Yeni crontab yazımı başarısız oldu." +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "En az {perc} boş düğümü korumaya çalışıyor" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" -"%(profile_id)s profilleri için Udev kuralı yüklenemedi. '%(dbus_interface)s' " -"DBus Servisi mevcut değil." +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Şimdi" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "{sshfs} bağlanamadı" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "ssh-agent bulunamadı. Lütfen kurulu olduğundan emin olun." -#: ../../common/encfstools.py:86 -#, python-format +#: common/sshtools.py:489 msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." msgstr "" +"Ssh özel anahtarının kilidi açılamadı. Parola yanlış ya da Cron için parola " +"yok." -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Uzak yol mevcut ancak bir dizin değil." -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Uzak yol yazılabilir değil." -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Uzak yol çalıştırılabilir değil." -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "Lütfen şifreyi onaylatın" +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Uzak yol oluşturulamadı." -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "Şifre eşleşmiyor" +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Uzak bilgisayar {host} {command} komutunu desteklemiyor" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" msgstr "" -"1.7.2 ve önceki encfs sürümlerinin --reverse seçeneğinde bir böcek var. " -"Lütfen encfs'yi güncelleyin." - -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "Anlık durum al" +"{host} bilgisayarındaki komutlar kontrol edildiğinde bilinmeyen bir hata " +"döndü" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Uzak sunucu {host} sabit bağlantıları desteklemiyor" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" +"\"{pubkey}\" genel SSH anahtarını uzak ana bilgisayar \"{host}\" üzerine " +"kopyala." -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "\"{user}\" kullanıcısı için parola giriniz." -#: ../../common/mount.py:590 -#, python-format +#: common/tools.py:382 +#, python-brace-format msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." msgstr "" +"{path} için girilen hedef dosya sistemi, Unix tipi dosya sistemleriyle " +"uyumsuz NTFS ile biçimlendirilmiş." -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} geçerli bir dizin değil." -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "Aşağıdaki dizin oluşturulamadı:" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " -msgstr "" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "Yazma erişimi kısıtlanmış olabilir." -#: ../../common/snapshotlog.py:62 +#: common/tools.py:471 +#, python-brace-format msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" -msgstr "" - -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" -msgstr "" - -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" -msgstr "" - -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "Tamamlandı" - -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." msgstr "" +"{path} için girilen hedef dosya sistemi hard-link desteklemeyen FAT ile " +"biçimlendirilmiş. Lütfen özgün bir Linux dosya sistemi kullanın." -#: ../../common/snapshots.py:669 +#: common/tools.py:482 +#, python-brace-format msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." -msgstr "Anlık durum klasörü bulunamadı." - -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "%s saniye bekliyor." - -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "" - -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "Bitiriyor" - -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "Dizin oluşturulamadı: %s" - -#: ../../common/snapshots.py:836 -msgid "Saving config file..." +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"{path} için girilen hedef dosya sistemi bir paylaşımlı SMB dizini. Lütfen " +"SMB sunucunun sembolik bağları desteklediğini teyit edin ya da " +"{expertOptions} bölümündeki {copyLinks} seçeneğini etkinleştirin." -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "... iznini kaydet" - -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Linkleri kopyala (sembolik bağlantıların referanslarını kaldırır)" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Uzman Seçenekleri" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." msgstr "" +"{path} için hedef dosya sistemi sshfs ile bağlanılmış bir paylaşımdır. Sshfs" +" sabit bağlantıları desteklemez. Lütfen 'SSH' modunu kullanın." -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "Klasör kaldırılamıyor: %s" - -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "Anlık durum al" - -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "Bu dizin içerisinde dosya oluşturulması sırasında hata:" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Back In Time Hakkında" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "Akıllı kaldırma" +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "Telif hakkı:" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "Eski anlık durumları sil" +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "Yazarlar:" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "En az boş alanı korumaya çalış" +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "Çevirmenler:" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" msgstr "" +"Onur Esat Karabacak\n" +"Taner Orak\n" +"Nuri Küçükler \n" +"Baris Ozyurt" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "HATALARLA!" - -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "Şimdi" +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "Yürürlükteki dil için çevirmen belirtimleri mevcut değil." -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "%s takılamadı" +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "bu bağlantı" -#: ../../common/sshtools.py:315 -msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." msgstr "" +"Bütün dillerdeki çevirmen belirtimleri için bu linki takip edin {thislink}." -#: ../../common/sshtools.py:345 -#, python-format -msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." -msgstr "" +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "Proje Web Sitesi" -#: ../../common/sshtools.py:377 -#, python-format -msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" -msgstr "" +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "Kullanım kılavuzu" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" msgstr "" +"Kullanım kılavuzunu tarayıcıda açın (mevcutsa yerel, aksi takdirde " +"çevrimiçi)" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" -msgstr "" +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}Versiyon{BOLDEND}: {version}" -#: ../../common/sshtools.py:470 -#, python-format +#: qt/app.py:332 +#, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"{app_name} appears to be running for the first time because no configuration" +" is found." msgstr "" +"{app_name} uygulaması, herhangi bir yapılandırma bulunmadığı için ilk kez " +"çalıştırılıyor gibi görünüyor." -#: ../../common/sshtools.py:472 -#, python-format +#: qt/app.py:337 msgid "" -"Remote path is not executable:\n" -" %s" +"Import an existing configuration from a backup location or another computer?" msgstr "" +"Yedekleme konumundan veya başka bir bilgisayardan mevcut bir yapılandırmayı " +"içe aktarmak ister misiniz?" -#: ../../common/sshtools.py:474 -#, python-format -msgid "" -"Couldn't create remote path:\n" -" %s" -msgstr "" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "Daha sonra Tamam’a basın." -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "Yedek oluştur" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format -msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." msgstr "" +"Dosya değişikliği tespiti için değişiklik zamanı ve boyutunu kullanın." -#: ../../common/sshtools.py:686 -#, python-format -msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "Yedek oluştur (sağlama toplamı kipi)" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" -msgstr "" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Dosya değişikliklerini algılamak için sağlama toplamlarını kullan." -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "Yedekleme sürecini duraklat" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "Yedekleme sürecini sürdür" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "Yedekleme sürecini durdur" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" -msgstr "" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "Yedekleme listesini yenile" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "Yedek isimlendir" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" -msgstr "" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "Yedeği kaldır" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" -msgstr "" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "Yedekleme günlüğünü görüntüle" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "Anlık durum ismi" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "Seçili yedeklemenin günlüğünü görüntüleyin." -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "Anlık durumu sil" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "En son yedekleme günlüğünü görüntüle" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" -msgstr "" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "En son yedekleme günlüğünü görüntüle." -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" -msgstr "" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Profilleri yönet…" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "Ayarlar" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Kullanıcı Geri Çağrısını Düzenle" -#: ../../qt/app.py:142 +#: qt/app.py:532 msgid "Shutdown" -msgstr "" +msgstr "Kapat" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "Yedekleme tamamlandığında sistemi kapat." -#: ../../qt/app.py:149 +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Kurulum dili…" + +#: qt/app.py:540 msgid "Exit" msgstr "Çıkış" -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "Yardım" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "man sayfası: Back In Time" + +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "\"Back In Time\" hakkındaki man sayfasını göster" -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "man sayfası: Profil yapılandırma dosyası" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" msgstr "" +"Profil yapılandırma dosyası (backintime-config) hakkında man sayfasını " +"görüntüler" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "Web sitesi" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "\"Back In Time\" web sitesini aç" -#: ../../qt/app.py:165 ../../qt/app.py:993 +#: qt/app.py:567 qt/app.py:2243 msgid "Changelog" +msgstr "Değişiklik Günlüğü" + +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" msgstr "" +"Değişiklik günlüğünü açın (mümkünse yerel olarak, aksi takdirde çevrimiçi)" -#: ../../qt/app.py:167 +#: qt/app.py:573 msgid "FAQ" -msgstr "" +msgstr "SSS" -#: ../../qt/app.py:169 +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "Sıkça Sorulan Sorular'ı (SSS) tarayıcıda açın" + +#: qt/app.py:577 msgid "Ask a question" -msgstr "" +msgstr "Soru Sor" -#: ../../qt/app.py:171 +#: qt/app.py:581 msgid "Report a bug" -msgstr "" +msgstr "Hata Bildir" + +#: qt/app.py:584 +msgid "Translation" +msgstr "Çeviri" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Çeviriye katılım mesajını tekrar gösterir." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Şifreleme Geçişi (EncFS)" -#: ../../qt/app.py:174 ../../qt/app.py:1439 +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "EncFS kaldırma mesajını tekrar gösterir." + +#: qt/app.py:599 msgid "About" msgstr "Hakkında" -#: ../../qt/app.py:204 +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Geri Yükle" + +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "Seçili dosyaları veya dizinleri orijinal konumlarına geri yükleyin." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Şuraya geri yükle …" + +#: qt/app.py:609 +msgid "Restore the selected files or directories to a new location." +msgstr "Seçili dosyaları veya dizinleri yeni bir konuma geri yükleyin." + +#: qt/app.py:615 +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "" +"Şu anda gösterilen dizini ve tüm içeriğini orijinal konumuna geri yükleyin." + +#: qt/app.py:621 +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "Şu anda gösterilen klasörü ve tüm içeriğini yeni hedefe geri yükle." + +#: qt/app.py:624 msgid "Up" msgstr "Yukarı" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 +#: qt/app.py:627 msgid "Show hidden files" msgstr "Gizli dosyaları göster" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "Geri Yükle" +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "Yedekleri karşılaştır…" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." -msgstr "" +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "Sürüm adayı" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." -msgstr "" +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "Bu Yayınlama Adayı hakkındaki mesajı tekrar gösterir." -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." -msgstr "" +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Yedekle" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." -msgstr "" +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Geri Yükle" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." -msgstr "" +#: qt/app.py:723 +msgid "&Help" +msgstr "Y&ardım" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "Anlık durumlar" +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "Sistem Tepsisi Simgesi" -#: ../../qt/app.py:260 -msgid "Snapshot" -msgstr "" +#: qt/app.py:780 +msgid "Automatic" +msgstr "Otomatik" -#: ../../qt/app.py:271 -msgid "View" -msgstr "" +#: qt/app.py:784 +msgid "Light icon" +msgstr "Açık Renk Simge" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "Kısayollar" +#: qt/app.py:785 +msgid "Dark icon" +msgstr "Koyu Renk Simge" -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" -msgstr "" +#: qt/app.py:808 +msgid "Icons only" +msgstr "Sadece simgeler" -#: ../../qt/app.py:398 -msgid "Add to Include" -msgstr "" +#: qt/app.py:811 +msgid "Text only" +msgstr "Sadece Metin" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" -msgstr "" +#: qt/app.py:814 +msgid "Text below icons" +msgstr "Metin ikonların altında" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "Metin ikonların yanında" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"Bu klasör,\n" +"seçili yedekte yok." -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"Anlık durum klasörü bulunamadı.\n" -"Eğer çıkarılabilir bir sürücüde ise lütfen takın ve TAMAM'a tıklayın" +"Bu klasör,\n" +"seçili yedekte yok." -#: ../../qt/app.py:527 +#: qt/app.py:995 msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." msgstr "" +"Eğer bu pencereyi kapatırsanız, Back In Time yedekleme işlemi " +"tamamlandığında sisteminizi kapatamayacak." -#: ../../qt/app.py:667 ../../qt/app.py:719 +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "Yine de pencere kapansın mı?" + +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Tamamlandı, yedekleme gerekmiyor" + +#: qt/app.py:1260 msgid "Working:" msgstr "Çalışıyor:" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "Bitti, yedekleme gerekmiyor" +#: qt/app.py:1267 +msgid "Working" +msgstr "Çalışıyor" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "Hata:" +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Hata" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 +#: qt/app.py:1314 qt/qtsystrayicon.py:295 msgid "Sent:" -msgstr "" +msgstr "Gönderilen:" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 +#: qt/app.py:1315 qt/qtsystrayicon.py:296 msgid "Speed:" -msgstr "" +msgstr "Hız:" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 +#: qt/app.py:1316 qt/qtsystrayicon.py:297 msgid "ETA:" -msgstr "" +msgstr "Tahmini Varış Süresi:" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "Evrensel" +#: qt/app.py:1498 +msgid "Backup:" +msgstr "Yedek:" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "Kök Dizini" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "{path} geri yükle" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "" - -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "Yedekleme klasörleri" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "{path} geri yükle, hedef …" -#: ../../qt/app.py:942 -#, python-format +#: qt/app.py:1624 +#, python-brace-format msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" - -#: ../../qt/app.py:1025 -#, python-format +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Merhaba\n" +"Back In Time'ı an itibarıyla {language} dilinde bir süredir kullanmaktasınız.\n" +"Yüklediğiniz Back In Time sürümünün {language} çevirisi henüz {perc} tamamlandı. Teknik uzmanlık seviyeniz ne olursa olsun çeviriye yardımcı olarak Back In Time'a katkıda bulunabilirsiniz.\n" +"Çeviriye katkıda bulunmak için {translation_platform_url} adresini ziyaret edebilirsiniz. Daha çok yardım ve soru için {back_in_time_project_website} adresini ziyaret edebilirsiniz.\n" +"Böldüğümüz için özür dileriz. Bu mesaj tekrar gösterilmeyecektir. Bu mesajı istediğiniz zaman yardım menüsünden görüntüleyebilirsiniz.\n" +"Back In Time Ekibi" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "çeviri platformu" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Web Sitesi" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Sizin çeviriniz" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "Mastodon'daki Fediverse: {link_and_label}." + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "{link_and_label} adresine e-posta gönderin." + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "E-posta listesi {link_and_label}." + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "Proje web sitesinde {link_and_label}." + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "hata bildir" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "Alternatif olarak istediğiniz iletişim yolunu kullanabilirsiniz." + +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" - -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"Back In Time'ın bu sürümü bir Sürüm Adayıdır ve öncelikli olarak bir sonraki resmi sürüme hazırlık olarak kararlılık testi için tasarlanmıştır..\n" +"Hiçbir kullanıcı verisi veya telemetri toplanmaz. Ancak, Back In Time ekibi Sürüm Adayı'nın kullanılıp kullanılmadığını ve bu tür ön sürüm sürümlerini sağlamaya devam etmenin değerli olup olmadığını bilmekle çok ilgilenmektedir.\n" +"Bu nedenle, ekip herhangi bir sorunla karşılaşmamış olsanız bile bu sürümü test edip etmediğinize dair kısa bir geri bildirim rica etmektedir. Birkaç dakikalık kısa bir test çalışması bile bize çok yardımcı olacaktır..\n" +"Aşağıdaki iletişim seçenekleri mevcuttur:\n" +"{contact_list}\n" +"Bu sürümde, bu mesaj tekrar gösterilmeyecek ancak yardım menüsünden istediğiniz zaman erişilebilir.\n" +"Desteğiniz ve Back In Time'ı geliştirmemize yardımcı olduğunuz için teşekkür ederiz!!\n" +"\"Back In Time\" ekibi" + +#: qt/app.py:1836 +#, python-brace-format msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." msgstr "" +"Hedefte yalnızca {free} boş alan mevcut ve bu alan tanımlanmış {threshold} " +"sınırının altında." -#: ../../qt/app.py:1038 -msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "Yedeklemeye devam edilsin mi?" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "{path} yolundaki bütün yeni dosyalar kaldırılacak. Devam edilsin mi?" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "Asıl dizindeki bütün yeni dosyalar kaldırılacak. Devam edilsin mi?" -#: ../../qt/app.py:1083 -#, python-format +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." msgstr "" +"{BOLD}UYARI{BOLDEND}: Dosyaları dosya sistemi kökünden silmek, sisteminizin " +"tamamını bozabilir." -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/app.py:2107 +msgid "Backup name" +msgstr "Yedek adı" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "Bu yedek kaldırılsın mı?" +msgstr[1] "Bu yedekler kaldırılsın mı?" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." msgstr "" +"Dil ayarları yalnızca Back In Time yeniden başlatıldıktan sonra etkili olur." -#: ../../qt/app.py:1105 +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "Sürümlü Yedeklemeler (root)" + +#: qt/backintime-qt-root.desktop:23 msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" +"Disk kullanımını azaltan, sürümlü yedeklemeler için kullanıcı dostu grafik " +"arayüzü (root modu)" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "Şu andaki disk içeriğini görüntüle" +#: qt/backintime-qt.desktop:14 +msgid "Versioned Backups" +msgstr "Sürümlü Yedeklemeler" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" +"Disk kullanımını azaltan, sürümlü yedeklemeler için kullanıcı dostu grafik " +"arayüzü" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "%s'da oluşturulan anlık durumu görüntüle" +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Soru" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" +#: qt/confirmrestoredialog.py:76 +#, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"Yerel öğelerin üzerine yazmadan veya kaldırmadan önce sonuna {suffix} " +"ekleyerek yedek kopyalar oluşturun." -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" msgstr "" +"Daha yeni dosya sürümleri, geri yüklenmeden önce sonuna {suffix} eklenerek " +"yeniden adlandırılacaktır. Artık onlara ihtiyacınız yoksa, aşağıdaki komutla" +" silebilirsiniz:" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" +#: qt/confirmrestoredialog.py:94 +#, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"Yalnızca var olmayan veya hedeftekilerden daha yeni olan öğeleri geri " +"yükleyin. \"{rsync_example}\" seçeneğini kullanın." -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "Özgün klasördeki daha yeni ögeleri kaldır." -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" +#: qt/confirmrestoredialog.py:135 +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Seçilen dosyaları veya klasörleri orijinal hedefe geri yükleyin ve yedekte " +"bulunmayan dosya veya klasörleri silin. Çok dikkatli olun çünkü bu işlem, " +"yedek alınırken hariç tutulan dosya ve klasörleri silecektir." + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"Bu öğeyi gerçekten {path} yeni klasörüne geri yüklemek istiyor musunuz?" +msgstr[1] "" +"Bu öğeleri gerçekten {path} yeni klasörüne geri yüklemek istiyor musunuz?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Bu öğeyi gerçekten geri yüklemek istiyor musunuz?" +msgstr[1] "Bu öğeleri gerçekten geri yüklemek istiyor musunuz?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "Kullanıcı geri araması: \"{filename}\"" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." msgstr "" +"Kullanıcı geri çağırma betiğinin ilk satırında bir shebang bulunmalıdır " +"(örn. {example})." -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Gizli dosyaları ve dizinleri göster/gizle (Ctrl+H)" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Dahil Et'e Ekle" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Dışla'ya Ekle" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" +msgstr[1] "" + +#: qt/fileview.py:313 +#, fuzzy +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" +"Sık kullanılan öğeler arasından seçim yaparak hariç tutma listesine eklemek " +"istediğiniz öğeleri belirleyin." +msgstr[1] "" +"Sık kullanılan öğeler arasından seçim yaparak hariç tutma listesine eklemek " +"istediğiniz öğeleri belirleyin." + +#: qt/fileview.py:320 +#, fuzzy +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" +"Sık kullanılan öğeler arasından seçim yaparak hariç tutma listesine eklemek " +"istediğiniz öğeleri belirleyin." +msgstr[1] "" +"Sık kullanılan öğeler arasından seçim yaparak hariç tutma listesine eklemek " +"istediğiniz öğeleri belirleyin." + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Kurulum dili" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Çevrilen: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Sistem öntanımlısı" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "İşletim sistemi dilini kullanın." + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "Yedekleme Günlük Görünümü" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Son Günlük Görünüm" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 msgid "Profile:" msgstr "Profil:" -#: ../../qt/logviewdialog.py:89 +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "Yedekler:" + +#: qt/logviewdialog.py:93 msgid "Filter:" -msgstr "" +msgstr "Filtrele:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Hata, [I] Bilgi, [C] Değiştir" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "kod çözümleme yolları" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 msgid "All" -msgstr "" +msgstr "Tümü" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Değişiklikler" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 msgid "Errors" -msgstr "" +msgstr "Hatalar" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "" +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "Bilgi" +msgstr[1] "Bilgiler" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "" +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync aktarımı başarısızlıkları (deneysel)" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "" +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Profilleri yönet" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Düzenle" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Ekle" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Kaldır" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Genel" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Dahil Et" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "Dışl&a" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "&Kaldırma & Saklama" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "Çalışıyor..." +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "&Seçenekler" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "Bugün" +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "&Uzman Seçenekleri" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "Dün" +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Yapılandırma Geri Yükle" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "Bu hafta" +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Yeni profil" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "Geçen hafta" +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Profili yeniden adlandır" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "\"{name}\" profili silinsin mi?" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "Sembolik bağlantıları dosya gibi kopyala" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" +"Yedekleekensembolik bağlantıları gerçek dosya veya dizin olarak kopyala. Tüm" +" bağlantıların mı yoksa yalnızca kaynak dizinin dışına işaret eden " +"bağlantıların mı kopyalanacağını seçiniz." -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "Düzenle" - -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "Bu seçenek yedekleme boyutunu artırabilir." -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "Varsayılan olarak devre dışı." -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" +"Tüm sembolik bağlantılar, işaret ettikleri gerçek dosyalar veya dizinlerle " +"değiştirilir. Bu işlem yedekleme boyutunu artırır ve aynı dosyaların birden " +"fazla kez saklanmasına neden olabilir." -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "Genel" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "'rsync --copy-links' komutu kullanılır." -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "Sadece harici" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:72 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" +"Yalnızca yedekleme kaynağının dışına işaret eden bağlantılar dosya olarak " +"kopyalanır. Bu, yedekleme boyutunu artırır ve aynı dosyaların birden fazla " +"kez saklanmasına neden olabilir." -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "'rsync --copy-unsafe-links' komutu kullanılır." -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "Editör ve geçici Ofis dosyaları" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:34 +msgid "Emacs backup files" +msgstr "Emacs yedekleme dosyaları" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:35 +msgid "Emacs autosave files" +msgstr "Emacs otomatik kaydetme dosyaları" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "Vim takas dosyaları" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "Microsoft Office geçici dosyaları" -#: ../../qt/settingsdialog.py:190 -msgid "Path:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "LibreOffice ve diğer OpenDocument düzenleyicileri dosyaları kilitler" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "Küçük Resimler ve Geçici Fotoğraflar" -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" +"GNU/Linux ve diğer Unix benzeri işletim sistemlerinde küçük resim önbelleği" -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "Windows'ta küçük resim veritabanı" -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "MacOS'ta meta veri dizini" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 -msgid "Password" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "Uygulamaya özel kilitler" -#: ../../qt/settingsdialog.py:255 -msgid "Save Password to Keyring" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "Discord uygulama kilidi dosyası" -#: ../../qt/settingsdialog.py:258 -msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "Discord oturum kilitleme dosyası" -#: ../../qt/settingsdialog.py:270 -msgid "Advanced" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "Discord kayıt kilitleme dosyası" + +#: qt/manageprofiles/excludesuggestions.py:62 +msgid "Caches & Temporary directories" +msgstr "Önbellekler ve Geçici dizinler" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "Kullanıcı uygulama önbelleği" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +msgid "System temporary directory" +msgstr "Sistem geçici dizini" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" +"Debian (veya Debian tabanlı) GNU/Linux dağıtımları için paket önbelleği" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "Flatpak uygulaması ve çalışma zamanı deposu" + +#: qt/manageprofiles/excludesuggestions.py:81 +msgid "System runtime directories" +msgstr "Sistem çalışma zamanı dizinleri" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "Çekirdek ve işlem bilgileri" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "Aygıt ve diğer donanım bilgileri (sysfs arayüzü)" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "Aygıt düğümleri" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "Çalışma zamanı sistem dosyaları" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "Diğer kalıcı olmayanlar" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "Şu anda bağlı olan dosya sistemlerinin listesi" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "Sistem takas dosyası (sanal bellek)" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "GNOME sanal dosya sistemi bağlama noktası" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "Kurtarılan dosya sistemi nesneleri" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "Çeşitli" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "Microsoft Windows'da meta veri dizini" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "Kullanıcı geri dönüşüm kutusu" + +#: qt/manageprofiles/excludesuggestions.py:112 +msgid "System backup files" +msgstr "Sistem yedekleme dosyaları" + +#: qt/manageprofiles/excludesuggestions.py:139 +msgid "Exclude Suggestions" +msgstr "Önerileri Hariç Tut" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" +"Yedekleme hariç tutma listesine eklemek için sık kullanılan öğeleri seçin." + +#: qt/manageprofiles/excludesuggestions.py:157 +msgid "Default" +msgstr "Varsayılan" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "Öntanımlı seçime sıfırla" -#: ../../qt/settingsdialog.py:307 +#: qt/manageprofiles/schedulewidget.py:38 msgid "Schedule" msgstr "Zamanlama" -#: ../../qt/settingsdialog.py:317 +#: qt/manageprofiles/schedulewidget.py:64 msgid "Day:" -msgstr "" +msgstr "Gün:" -#: ../../qt/settingsdialog.py:328 +#: qt/manageprofiles/schedulewidget.py:69 msgid "Weekday:" -msgstr "" +msgstr "Haftanın günü:" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "Saat:" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "Zaman:" -#: ../../qt/settingsdialog.py:350 +#: qt/manageprofiles/schedulewidget.py:79 msgid "Hours:" +msgstr "Saatler:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "bir saat sonra" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "Dakika:" + +#: qt/manageprofiles/schedulewidget.py:93 +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." msgstr "" +"Sürücü bağlanır bağlanmaz Back In Time'ı çalıştır (yalnızca X günde bir). " +"Sizden sudo parolası istenecektir." -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" +"Back In Time'ı tekrarlayarak çalıştır. Bu, bilgisayar düzenli olarak " +"çalışmıyorsa kullanışlıdır." -#: ../../qt/settingsdialog.py:364 +#: qt/manageprofiles/schedulewidget.py:110 msgid "Every:" +msgstr "Her:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "Hata ayıklama mesajlarının kaydetmeyi etkinleştir" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" +"Sorun giderme düzeyindeki iletileri '--debug' seçeneğiyle sistem günlüğüne " +"yazar." -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." msgstr "" +"Dikkat: Yüksek miktarda çıktı yaratacağından sadece geçici bir süreliğine " +"tanılama yapmak için kullanın." -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "Dahil et" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Devre dışı" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Her Başlatıldığında/Yeniden Başlatıldığında" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Dakikada bir" +msgstr[1] "Her {n} dakikada bir" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Her Saat" +msgstr[1] "Her {n} saatte" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "Dosya ekle" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Özel saatler" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "Klasör ekle" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Her gün" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Tekrar tekrar (anacron)" -#: ../../qt/settingsdialog.py:434 -msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Sürücü bağlandığında (udev)" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Her hafta" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Her ay" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Her yıl" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Saat" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Gün" -#: ../../qt/settingsdialog.py:488 -#, python-format +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Hafta" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Ay" + +#: qt/manageprofiles/schedulewidget.py:326 msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." msgstr "" +"Özel saatler yalnızca virgülle ayrılmış saat listesi (ör. 8,12,18, 23) ya da" +" her 3 saatte bir düzenli yedekleme için */3 olabilir." -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "Oto-kaldır" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "<Вибрати інший файл…>" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "день(днів)" +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "Вибрати існуючий файл приватного ключа з іншого місця." -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "<Створити нову пару ключів…>" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "Створити новий ключ SSH без парольної фрази." -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "тижнів" +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "Повний шлях: {path}" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "" +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "Приватний ключ:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "місяців" +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "Використовувати системну конфігурацію SSH" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" +#: qt/manageprofiles/sshkeyselector.py:212 +msgid "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." msgstr "" +"Залишає файл ключа невибраним. Підключення по SSH залежатимуть від " +"конфігурації клієнта системи (напр., ~/.ssh/config)." -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "Не видаляти названі резервні копії" - -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "Параметри" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "Проксі-сервер SSH" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "Увімкнути сповіщення" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "Сервер:" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "Вимкнути резервування при роботі від батареї" +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "Порт:" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "Статус живлення не доступний" +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "Користувач:" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" +#: qt/manageprofiles/sshproxywidget.py:71 +msgid "" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"Підключайтеся до цільового сервера через цей проксі (також відомий як " +"проміжний сервер або jump host). Докладніше див. розділ «-J» у документації " +"до команди «ssh» або розділ «ProxyJump» у посібнику «ssh_config»." -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format +msgid "" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." +msgstr "" +"{BOLD}Підказка{ENDBOLD}: в режимі «SSH зашифрований» можна використовувати " +"лише одну або дві зірочки (напр., {example2}). Інші символи підстановки і " +"шаблони ігноруються (напр., {example1}). Імена файлів у цьому режимі " +"непередбачувані через шифрування EncFS." + +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "Виключити шаблони, файли або каталоги" + +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "Додати шаблон" + +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "Додати файли" + +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "Додати каталоги" + +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "Пропозиції" + +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "Додайте до списку виключень часто використовувані елементи." + +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "Виключити файли більші, ніж:" + +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "Виключити файли більші, ніж значення в {size_unit}." + +#: qt/manageprofiles/tab_exclude.py:140 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"Якщо «Full rsync mode» вимкнено, цей параметр вплине лише на новостворені " +"файли, оскільки для rsync це опція перенесення, а не правило виключення. " +"Тому великі файли, зарезервовані раніше, залишаться в резервних копіях, " +"навіть якщо ці файли буде змінено." -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "Виключити за шаблоном" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "Ігнорувати помилки (зберігати незавершені копії)" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "Уведіть шаблон виключення:" -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "Довідку див. у розділі {link} посібника rsync." -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "Рівень ведення журналу:" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "Відкриває посібник rsync" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "Нічого" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "Виключити файли" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "Зміни та помилки" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "Виключити каталоги" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "Змінюйте ці налаштування тільки у випадку, якщо Ви знаєте що робите!" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "Вимкнено, оскільки цей шаблон не працює у режимі «SSH зашифрований»." -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." msgstr "" +"Це параметри для просунутого налаштування. Змінюйте їх, тільки якщо повністю" +" усвідомлюєте їхню дію." + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "Запустити «rsync» з «{cmd}»:" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 msgid "as cron job" -msgstr "" +msgstr "як cron job" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 msgid "on remote host" -msgstr "" +msgstr "на віддаленому сервері" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "під час резервування вручну" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" - -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "Встановіть «nocache», щоб увімкнути цей параметр." -#: ../../qt/settingsdialog.py:714 +#: qt/manageprofiles/tab_expert_options.py:116 msgid "on local machine" -msgstr "" +msgstr "на локальному комп’ютері" -#: ../../qt/settingsdialog.py:721 +#: qt/manageprofiles/tab_expert_options.py:130 msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "Перенаправити stdout на /dev/null в cronjobs." + +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." msgstr "" +"Якщо встановлено MTA, Cron автоматично надішле електронного листа з " +"прикріпленим виводом cronjobs." -#: ../../qt/settingsdialog.py:727 +#: qt/manageprofiles/tab_expert_options.py:142 msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +msgstr "Перенаправити stderr на /dev/null в cronjobs." -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." msgstr "" +"Якщо встановлено MTA, Cron автоматично надішле електронного листа з " +"прикріпленими помилками cronjobs." -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "кбіт/с" + +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "Обмежити пропускну здатність:" -#: ../../qt/settingsdialog.py:774 +#: qt/manageprofiles/tab_expert_options.py:204 msgid "Preserve ACL" msgstr "Зберігати ACL" -#: ../../qt/settingsdialog.py:787 +#: qt/manageprofiles/tab_expert_options.py:222 msgid "Preserve extended attributes (xattr)" msgstr "Зберігати додаткові атрибути (xattr)" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "" -"Копіювати ненадійні посилання (працює лише з абсолютними посиланнями)" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "Обмежити однією файловою системою" -#: ../../qt/settingsdialog.py:836 +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "Параметри необхідно брати в лапки, напр. {example}." + +#: qt/manageprofiles/tab_expert_options.py:276 msgid "Paste additional options to rsync" -msgstr "" +msgstr "Додаткові параметри для rsync" + +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "Префікс для виконання перед кожною командою на віддаленому сервері." -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"Екрануйте змінні за допомогою \\$FOO. Це не стосується rsync. Щоб додати " +"префікс для rsync, використовуйте «{example_value}» з {rsync_options_value}." -#: ../../qt/settingsdialog.py:849 +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "за замовчуванням" + +#: qt/manageprofiles/tab_expert_options.py:298 msgid "Add prefix to SSH commands" -msgstr "" +msgstr "Додати префікс до команд SSH" -#: ../../qt/settingsdialog.py:852 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "Перевіряти, чи сервер онлайн" + +#: qt/manageprofiles/tab_expert_options.py:311 msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." msgstr "" +"Увага: якщо вимкнено, а сервер недоступний, це може призвести до " +"непередбачуваних помилок." -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "Перевіряти, чи підтримує сервер усі необхідні команди." -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" +#: qt/manageprofiles/tab_expert_options.py:318 +msgid "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." msgstr "" +"Увага: якщо вимкнено, а сервер не підтримує всіх необхідних команд, це може " +"призвести до непередбачуваних помилок." + +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(за замовчуванням: {})" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "вимкнено" + +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "увімкнено" -#: ../../qt/settingsdialog.py:871 +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "Режим:" + +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "Де зберігати резервні копії" + +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "Налаштування SSH" + +#: qt/manageprofiles/tab_general.py:132 +msgid "Path:" +msgstr "Шлях:" + +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "Файл ключа:" + +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 +msgid "Password" +msgstr "Пароль" + +#: qt/manageprofiles/tab_general.py:206 +msgid "Save Password to Keyring" +msgstr "Зберегти пароль у зв’язці ключів" + +#: qt/manageprofiles/tab_general.py:210 +msgid "Cache Password for Cron (Security issue: root can read password)" +msgstr "Кешувати пароль для Cron (Увага: root може читати пароль)" + +#: qt/manageprofiles/tab_general.py:226 +msgid "Advanced" +msgstr "Додатково" + +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "Повний шлях до копії:" + +#: qt/manageprofiles/tab_general.py:264 msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." msgstr "" +"Планування вимкнено, оскільки не знайдено інсталяції cron. Встановіть cron, " +"щоб увімкнути резервне копіювання за розкладом." -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "Шлях до місця призначення резервної копії не може бути порожнім." -#: ../../qt/settingsdialog.py:875 +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "Пароль шифрування не може бути порожнім." + +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" msgstr "" +"Помилка під час спроби входу на віддалений сервер. Повідомлення про помилку:" -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" +#: qt/manageprofiles/tab_general.py:557 +msgid "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." msgstr "" +"Щоб увімкнути безпарольний доступ, можна скопіювати публічний ключ SSH на " +"віддалений сервер." -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "Продовжити копіювання ключа SSH?" -#: ../../qt/settingsdialog.py:908 +#: qt/manageprofiles/tab_general.py:585 msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." msgstr "" +"Не вдалося скопіювати публічний ключ SSH. Причиною може бути проблема з " +"підключенням або правами доступу." -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "Новий профіль" +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "Автентичність сервера {host} не вдалося встановити." -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "Перейменувати профіль" +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "Відбиток ключа {keytype}:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "Перевірте цей відбиток. Додати його до файлу «known_hosts»?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "Справді змінити каталог для резервних копій?" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "Ви впевнені, що хочите знищити профіль \"%s\" ?" +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "Вибране місце призначення резервної копії не порожнє." -#: ../../qt/settingsdialog.py:1201 +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "Щоб використовувати шифрування, воно повинно бути порожнім." + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "Недійсний файл: не приватний ключ SSH" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." msgstr "" +"Вибраний файл ({path}) є публічним ключем SSH. Будь ласка, виберіть " +"відповідний файл приватного ключа (без «.pub»)." -#: ../../qt/settingsdialog.py:1237 +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" +"The file {path} already exists. Cannot create a new SSH key with that name." msgstr "" +"Файл {path} вже існує. Неможливо створити новий ключ SSH з такою назвою." -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "Не вдалося створити новий ключ SSH у {path}." -#: ../../qt/settingsdialog.py:1360 +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "Включити файли і каталоги" + +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." msgstr "" +"«{path}» — символьне посилання. Об’єкт, на який воно вказує, не буде " +"зарезервовано, якщо його не включити." + +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "Включити в резервну копію цільовий об’єкт символьного посилання?" + +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "Включити файли" + +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "Включити каталоги" + +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "Увімкнути сповіщення" + +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "Вимкнути резервування при роботі від батареї" + +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "Статус живлення недоступний" -#: ../../qt/settingsdialog.py:1376 -#, python-format +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "Створювати лише одну резервну копію за раз" + +#: qt/manageprofiles/tab_options.py:56 msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." msgstr "" +"Інші резервні копії буде заблоковано, доки триває поточне резервування. Це " +"глобальне налаштування, тобто воно вплине на всі профілі цього користувача. " +"Однак для інших користувачів його потрібно вмикати окремо." + +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "Під час відновлення зберігати копії переміщених файлів" -#: ../../qt/settingsdialog.py:1384 +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "Ігнорувати помилки (зберігати незавершені копії)" + +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "Виявляти зміни за контрольними сумами" + +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "Резервувати незалежно від того, чи були зміни." + +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "Попереджати, якщо вільного місця менше, ніж" + +#: qt/manageprofiles/tab_options.py:101 msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." msgstr "" +"Попереджає, коли вільного місця на диску призначення резервної копії менше " +"за вказане значення." -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "Виключити по шаблону" +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." +msgstr "" +"Якщо ввімкнено політику Видалення і збереження і старі резервні копії " +"видаляються на основі наявного вільного місця, це значення не може бути " +"меншим за встановлене в політиці." -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "Виключити файл" +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "Рівень журналу:" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "Виключити каталог" +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "Нічого" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "Включить файл" +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "iнструкції користувача" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." msgstr "" +"Наведені нижче правила обробляються зверху вниз. Пізніші правила мають " +"перевагу над попередніми. Подробиці та приклади див. в {manual_link}." -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "Включить каталог" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "Відкрити посібник користувача в браузері." -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "Ви впевнені, що бажаєте змінити папку для знімків системи?" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "Не видаляти найновішу резервну копію." -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "Найновіша резервна копія зберігається за будь-яких обставин." -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "Цю поведінку неможливо змінити." -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "Не видаляти названі резервні копії." + +#: qt/manageprofiles/tab_remove_retention.py:258 +msgid "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." msgstr "" +"Резервні копії, яким, окрім звичайної позначки часу, було присвоєно ім’я, " +"будуть збережені за будь-яких обставин і не видалятимуться." + +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "років" + +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "Видаляти копії, старіші за" + +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "Повні дні. Поточний день не враховується." -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." msgstr "" +"Календарні тижні, що починаються з понеділка. Поточний тиждень не " +"враховується." + +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "Періоди по 12 місяців. Поточний місяць не враховується." + +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "Політика збереження" + +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "Виконувати у фоні на сервері." -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." msgstr "" +"Політика збереження виконуватиметься безпосередньо на віддаленому " +"комп’ютері, а не локально. На віддаленому комп’ютері мають бути встановлені " +"і доступні команди «bash», «screen» і «flock»." + +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "Якщо вибрано, Back In Time спочатку протестує віддалений комп’ютер." + +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "Відлік днів ведеться від сьогоднішнього дня." + +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "Зберігати всі копії за останні" + +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "днів." + +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "Зберігати останню копію за кожен день за останні" -#: ../../qt/settingsdialog.py:1799 -#, python-format +#: qt/manageprofiles/tab_remove_retention.py:344 msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." +"The weeks are counted starting from the current running week. A week starts " +"on Monday." msgstr "" +"Відлік тижнів, починаючи з поточного. Тиждень починається з понеділка." + +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "Зберігати останню копію за кожен тиждень за останні" + +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "тижнів." + +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "Відлік календарних місяців, починаючи з поточного місяця." + +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "Зберігати останню копію за кожен місяць за останні" + +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "місяців." + +#: qt/manageprofiles/tab_remove_retention.py:370 +msgid "" +"The years are counted as calendar years starting with the current year." +msgstr "Відлік календарних років, починаючи з поточного року." + +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "Зберігати останню копію за кожен рік за" + +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "всі роки." + +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… вільного місця менше, ніж" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… вільних айнодів менше, ніж" + +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "Видаляти найстарішу копію, якщо…" + +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "Для запуску Back In Time від імені root необхідна автентифікація." + +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 +msgid "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." msgstr "" +"Для зміни розкладів резервного копіювання, що запускаються підключенням " +"зовнішнього сховища, потрібна автентифікація." + +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "Скорочення" + +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "Місця" + +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "Файлова система" + +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "Каталоги резервних копій" + +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "Профіль: {profile_name}" + +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "Профіль: {profile_name} (користувача «{desktop_user}»)" + +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "Переглянути останній журнал" + +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "Запуск {appname}" + +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "Виконання…" + +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "man page: {man_page_name}" + +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "Імпортувати конфігурацію" + +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "Не вибрано каталог" + +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "Імпорт" + +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "Пошук…" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" msgstr "" +"Виберіть каталог резервних копій, з якого слід імпортувати файл " +"конфігурації. Шлях може мати такий вигляд: {samplePath}" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." +#: qt/restoreconfigdialog.py:216 +msgid "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." msgstr "" +"Якщо це каталог на зовнішньому або віддаленому диску, його потрібно " +"попередньо змонтувати вручну." + +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "Сканувати ще раз" + +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "Показати приховані каталоги" + +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "Показати/сховати приховані каталоги (Ctrl+H)" + +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "Не знайдено конфігурації в цьому каталозі" + +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "Пошук завершено." + +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "Показати повний журнал" + +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "Відлік до вимкнення" + +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "Резервування завершено." + +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "Скасувати вимкнення" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Параметри diff" +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "Вимкнути зараз" -#: ../../qt/snapshotsdialog.py:60 +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "Система завершить роботу через {n} секунду." +msgstr[1] "Система завершить роботу через {n} секунди." +msgstr[2] "Система завершить роботу через {n} секунд." + +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "Параметри порівняння резервних копій" + +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "Команда:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "Параметри:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" -msgstr "Використовувати %1 та %2 до пааметрів шляху" +msgstr "Використовуйте %1 та %2 як параметри шляху" + +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "Задайте команду diff або натисніть «Скасувати»." -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "Список тільки знімків, що розрізняються" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "" +"Не вдається знайти команду «{cmd}» у цій системі. Будь ласка, спробуйте щось" +" інше або натисніть «Скасувати»." -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" +"Для команди diff не задано жодних параметрів. Використовується значення за " +"замовчуванням «{params}»." + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "Резервні копії" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "Тільки копії, що відрізняються" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "Показати лише копії, однакові з:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "Глибока перевірка (більш точна, але повільніша)" +msgstr "Глибока перевірка (точніша, але повільна)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" -msgstr "" +msgstr "Видалити" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "" +msgstr "Вибрати все" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Відмінності" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "Порівняти" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "Перейти до" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "Неможна порівнювати резервну копію саму з собою" - -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "Команду не знайдено: %s" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "Налаштування" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:424 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." msgstr "" +"Неможливо порівняти резервну копію саму з собою, таке порівняння було б " +"зайвим." + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "Справді видалити {file_or_dir} з резервної копії {backup_id}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "Справді видалити {file_or_dir} з {count} резервних копій?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "УВАГА: це не можна буде скасувати." + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "Виключити {path} з майбутніх резервних копій?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "Режим root" + +#: qt/statusbar.py:87 +msgid "" +"Back In Time is currently running with root privileges (full system access)" +msgstr "Back In Time наразі працює з правами root (повний доступ до системи)" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "Сьогодні" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "Вчора" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "Цього тижня" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "Попереднього тижня" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "Остання перевірка {time}" + +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "Це НЕ резервна копія, а Ваші файли на комп’ютері." diff --git a/common/po/updatelaunchpad.sh b/common/po/updatelaunchpad.sh deleted file mode 100755 index bc254fa23..000000000 --- a/common/po/updatelaunchpad.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -#EXCEPTIONS="es.po de.po" -EXCEPTIONS="none" -TRANSLATIONFILE="launchpad-export.tar.gz" - -rm -rf tmp -mkdir tmp -tar xfz $TRANSLATIONFILE -C tmp - -for popath in `find tmp -name \*.po`; do - #echo $popath - #lang=`basename $popath | cut -d- -f4` - lang=`basename $popath | cut -d- -f2` - #echo $lang - - ignore="0" - for exception in $EXCEPTIONS; do - if [ $lang = $exception ]; then - ignore="1" - break - fi - done - - if [ $ignore = "1" ]; then - echo "Ignore $lang" - else - if [ -f $lang ]; then - echo "rm $lang" - rm $lang - fi - - echo "cp $popath $lang" - cp $popath $lang - fi -done - -rm -rf tmp -rm $TRANSLATIONFILE - diff --git a/common/po/vi.po b/common/po/vi.po new file mode 100644 index 000000000..0c05664a3 --- /dev/null +++ b/common/po/vi.po @@ -0,0 +1,2608 @@ +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Hai Nam +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. +msgid "" +msgstr "" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2025-04-10 10:35+0000\n" +"Last-Translator: buhtz \n" +"Language-Team: Vietnamese \n" +"Language: vi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.10.2\n" + +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." +msgstr "" + +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "Cảnh báo" + +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "Hồ sơ chính" + +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "Cục bộ (được mã hóa bằng EncFS)" + +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH (được mã hóa EncFS)" + +#: common/config.py:237 +msgid "Local" +msgstr "Cục bộ" + +#: common/config.py:240 +msgid "Local encrypted" +msgstr "Mã hóa cục bộ" + +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "Mã hóa" + +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" + +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "Khóa SSH riêng" + +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "Hồ sơ: “{name}”" + +#: common/config.py:301 +#, fuzzy +msgid "Backup directory is not valid." +msgstr "Thư mục snapshot không hợp lệ." + +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "Phải chọn ít nhất một thư mục để sao lưu." + +#: common/config.py:331 common/config.py:346 +#, fuzzy, python-brace-format +msgid "Directory: {path}" +msgstr "Khôi phục {path}" + +#: common/config.py:332 common/config.py:347 +#, fuzzy +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "Thư mục con sao lưu không thể được thêm vào." + +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "" + +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "" + +#: common/config.py:1515 +#, fuzzy, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "Lịch trình theo udev không hoạt động trong chế độ {mode}" + +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "Ghi crontab mới thất bại." + +#: common/config.py:1577 +#, fuzzy +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"Cron không hoạt động mặc dù lệnh crontab có sẵn. Các công việc sao lưu đã " +"lên lịch sẽ không chạy. Có thể cron đã được cài đặt nhưng chưa được kích " +"hoạt. Hãy thử lệnh \"systemctl enable cron\" hoặc tham khảo các kênh hỗ trợ " +"của bản phân phối GNU Linux của bạn." + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "Lưu cấu hình thất bại" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "Nạp cấu hình thất bại" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "Hồ sơ \"{name}\" đã tồn tại." + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "Hồ sơ cuối cùng không thể bị xóa bỏ." + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, fuzzy, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "Không thể mount '{command}'" + +#: common/encfstools.py:190 +#, fuzzy +msgid "Configuration for the encrypted directory not found." +msgstr "Không tìm thấy cấu hình cho thư mục đã mã hóa." + +#: common/encfstools.py:197 +#, fuzzy +msgid "Create a new encrypted directory?" +msgstr "Tạo một thư mục được mã hóa mới?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "Hủy" + +#: common/encfstools.py:209 +#, fuzzy +msgid "Please re-enter the EncFS password to confirm." +msgstr "Vui lòng nhập mật khẩu cho “{user}”." + +#: common/encfstools.py:215 +#, fuzzy +msgid "The EncFS passwords do not match." +msgstr "Mật khẩu không trùng khớp." + +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "Chụp snapshot" + +#: common/gocryptfstools.py:133 +#, fuzzy, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "Không thể mount '{command}'" + +#: common/mount.py:663 +#, fuzzy, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "Không thể unmount {mountprocess} từ {mountpoint}." + +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "" +"Không tìm thấy {command}. Xin hãy cài đặt (ví dụ: thông qua " +"{installcommand})" + +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "Vị trí mount {mntpoint} không trống." + +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "Nhập mật khẩu cho hồ sơ {mode} \"{profile}\":" + +#: common/schedule.py:238 +#, fuzzy, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "" +"Không thể cài đặt lệnh Udev cho hồ sơ {profile_id} vì Dịch vụ DBus " +"“{dbus_interface}” không khả dụng" + +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "Không tìm thấy UUID cho {path}" + +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "THẤT BẠI" + +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "Khôi phục quyền hạn" + +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "Xong" + +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "" + +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "Trì hoãn sao lưu khi đang sử dụng pin" + +#: common/snapshots.py:931 qt/app.py:377 +#, fuzzy +msgid "Can't find backup directory." +msgstr "Không tạo được thư mục" + +#: common/snapshots.py:935 qt/app.py:378 +#, fuzzy +msgid "If it is on a removable drive, please plug it in." +msgstr "" +"Không thể tìm thấy thư mục snapshots.\n" +"Nếu nó nằm trên ổ cứng có thể tháo rời thì vui lòng cắm vào máy rồi ấn nút OK." + +#: common/snapshots.py:938 +#, fuzzy, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "Đang chờ %s giây." + +#: common/snapshots.py:1005 +#, fuzzy, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "Chụp snapshot {snapshot_id} thất bại." + +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "" + +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "Không tạo được thư mục." + +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "Đang lưu tập tin cấu hình…" + +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "Đang lưu quyền hạn…" + +#: common/snapshots.py:1377 +#, fuzzy, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "Tìm thấy {snapshot_id} đang dang dở và có thể tiếp tục." + +#: common/snapshots.py:1401 +#, fuzzy, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "Đang xóa thư mục {snapshot_id} dang dở từ lần chạy trước" + +#: common/snapshots.py:1412 +#, fuzzy +msgid "Can't remove directory" +msgstr "Không thể xóa thư mục" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "" + +#: common/snapshots.py:1517 +msgid "Success" +msgstr "Thành công" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "Truyền một phần do lỗi" + +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "Truyền một phần do file nguồn biến mất (xem 'man rsync')" + +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "'rsync' kết thúc với mã kết thúc {exit_code}" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "Xem 'man rsync' để biết nhiều chi tiết hơn" + +#: common/snapshots.py:1545 +#, fuzzy +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "Mã kết thúc rsync âm là số chỉ thị, xem 'kill -l' và 'man kill'" + +#: common/snapshots.py:1566 +#, fuzzy +msgid "Nothing changed, no new backup necessary" +msgstr "Không có gì thay đổi, không cần snapshot mới" + +#: common/snapshots.py:1611 +#, fuzzy, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "Không thể đổi tên {new_path} thành {path}" + +#: common/snapshots.py:1998 +#, fuzzy +msgid "Applying rules to remove old backups" +msgstr "Đang xóa các snapshot cũ" + +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "" + +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "Đang cố gắng giữ dung lượng trống tối thiểu" + +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "Đang cố gắng giữ tổi thiểu {perc} inode trống" + +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "Vừa mới đây" + +#: common/sshtools.py:233 +#, fuzzy, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "Không thể mount {sshfs}" + +#: common/sshtools.py:306 +#, fuzzy +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "Không tìm thấy ssh-agent. Đảm bảo rằng nó đã được cài." + +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "" +"Không thể mở khóa SSH riêng. Mật khẩu bị sai hay không thể truy cập bởi " +"cron." + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "Đường dẫn từ xa tồn tại nhưng không phải là một thư mục." + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "Đường dẫn từ xa không thể được viết vào." + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "Đường dẫn từ xa không thể được thực thi." + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "Không tạo được đường dẫn từ xa." + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "Máy từ xa {host} không hỗ trợ {command}" + +#: common/sshtools.py:1026 +#, fuzzy, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "Lệnh kiểm tra trên máy khách {host} trả về lỗi không xác định" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "Máy ở xa {host} không hỗ trợ harklink" + +#: common/sshtools.py:1206 +#, fuzzy, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "Sao chép khóa ssh công khai \"{pubkey}\" đến máy chủ từ xa \"{host}\"" + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "Vui lòng nhập mật khẩu cho “{user}”." + +#: common/tools.py:382 +#, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." +msgstr "" +"Hệ thống tập tin đích cho “{path}” được định dạng bằng chuẩn NTFS, chuẩn này" +" không hỗ trợ liên kết cứng. Vui lòng dùng một hệ thống tập tin được Linux " +"hỗ trợ." + +#: common/tools.py:414 +#, fuzzy, python-brace-format +msgid "{path} is not a valid directory." +msgstr "Đường dẫn {path} không phải là một thư mục." + +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "" + +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "" + +#: common/tools.py:471 +#, fuzzy, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." +msgstr "" +"Hệ thống tập tin đích cho “{path}” được định dạng bằng chuẩn FAT, chuẩn này " +"không hỗ trợ liên kết cứng. Vui lòng dùng một hệ thống tập tin được Linux hỗ" +" trợ." + +#: common/tools.py:482 +#, fuzzy, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." +msgstr "" +"Hệ thống tập tin đích cho {path} là một phần gắn kết SMB. Vui lòng đảm bảo " +"máy chủ SMB từ xa hỗ trợ liên kết tượng trưng hoặc kích hoạt {copyLinks} " +"trong {expertOptions}." + +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "Sao chép liên kết (truy cập liên kết tượng trưng)" + +#: common/tools.py:487 +msgid "Expert Options" +msgstr "Tùy chọn dành cho chuyên gia" + +#: common/tools.py:491 +#, fuzzy, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." +msgstr "" +"Hệ thống tập tin đích cho {path} là một phần gắn kết sshfs. Sshfs không hỗ " +"trợ liên kết cứng. Vui lòng sử dụng chế độ 'SSH'." + +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "" + +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "" + +#: qt/aboutdlg.py:92 +#, fuzzy +msgid "Authors:" +msgstr "Tác giả" + +#: qt/aboutdlg.py:96 +#, fuzzy +msgid "Translators:" +msgstr "Phiên dịch" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Hai Nam" + +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "" + +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "" + +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "" + +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "" + +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "" + +#: qt/app.py:332 +#, fuzzy, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." +msgstr "" +"Có vẻ như {app_name} đang chạy lần đầu tiên vì không tìm thấy cấu hình nào." + +#: qt/app.py:337 +#, fuzzy +msgid "" +"Import an existing configuration from a backup location or another computer?" +msgstr "Nhập cấu hình hiện có (từ thư mục sao lưu hoặc từ máy tính khác)?" + +#: qt/app.py:379 +msgid "Then press OK." +msgstr "" + +#: qt/app.py:483 +msgid "Create a backup" +msgstr "" + +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "Dùng thời gian thay đổi & kích cỡ để dò sự thay đổi của file." + +#: qt/app.py:488 +#, fuzzy +msgid "Create a backup (checksum mode)" +msgstr "Chụp snapshot với checksum" + +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "Dùng checksum để phát hiện sự thay đổi của file." + +#: qt/app.py:492 qt/qtsystrayicon.py:125 +#, fuzzy +msgid "Pause backup process" +msgstr "Tạm dừng quá trình chụp snapshot" + +#: qt/app.py:496 qt/qtsystrayicon.py:130 +#, fuzzy +msgid "Resume backup process" +msgstr "Tiếp tục quá trình chụp snapshot" + +#: qt/app.py:500 qt/qtsystrayicon.py:135 +#, fuzzy +msgid "Stop backup process" +msgstr "Dừng hẳn quá trình chụp snapshot" + +#: qt/app.py:504 +#, fuzzy +msgid "Refresh backup list" +msgstr "Tải lại danh sách snapshot" + +#: qt/app.py:508 +msgid "Name backup" +msgstr "" + +#: qt/app.py:512 +#, fuzzy +msgid "Remove backup" +msgstr "Xóa snapshot" + +#: qt/app.py:516 +#, fuzzy +msgid "Open backup log" +msgstr "Xem nhật ký cuối cùng" + +#: qt/app.py:518 +#, fuzzy +msgid "View log of the selected backup." +msgstr "Phải chọn ít nhất một thư mục để sao lưu." + +#: qt/app.py:520 +#, fuzzy +msgid "Open last backup log" +msgstr "Xem nhật ký cuối cùng" + +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "" + +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "Quản lý hồ sơ…" + +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "Sửa user-callback" + +#: qt/app.py:532 +msgid "Shutdown" +msgstr "Tắt máy" + +#: qt/app.py:534 +#, fuzzy +msgid "Shut down system after backup has finished." +msgstr "Tắt máy sau khi chụp snapshot hoàn tất." + +#: qt/app.py:536 +msgid "Setup language…" +msgstr "Cài đặt ngôn ngữ…" + +#: qt/app.py:540 +msgid "Exit" +msgstr "Thoát" + +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "" + +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "" + +#: qt/app.py:555 +#, fuzzy +msgid "man page: Profiles config file" +msgstr "Tập tin cấu hình hồ sơ" + +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "" + +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "" + +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "Nhật ký thay đổi" + +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "" + +#: qt/app.py:573 +msgid "FAQ" +msgstr "Câu hỏi thường gặp" + +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "" + +#: qt/app.py:577 +msgid "Ask a question" +msgstr "Đặt câu hỏi" + +#: qt/app.py:581 +msgid "Report a bug" +msgstr "Báo lỗi" + +#: qt/app.py:584 +msgid "Translation" +msgstr "Phiên dịch" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "Hiển thị lại thông báo về việc tham gia dịch thuật." + +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "Chuyển đổi mã hóa (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "Hiển thị lại thông báo về việc gỡ bỏ EncFS." + +#: qt/app.py:599 +msgid "About" +msgstr "Giới thiệu" + +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "Khôi phục" + +#: qt/app.py:604 +#, fuzzy +msgid "Restore the selected files or directories to the original location." +msgstr "Khôi phục các tập tin và thư mục được chọn về vị trí ban đầu." + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "Khôi phục đến …" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "Khôi phục các tập tin và thư mục được chọn đến vị trí mới." + +#: qt/app.py:615 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "" +"Khôi phục thư mục hiện tại và tất cả nội dung bên trong nó về vị trí ban " +"đầu." + +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "" +"Khôi phục thư mục hiện tại và tất cả nội dung bên trong nó đến vị trí mới." + +#: qt/app.py:624 +msgid "Up" +msgstr "Lên" + +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "Hiện tập tin ẩn" + +#: qt/app.py:630 +#, fuzzy +msgid "Compare backups…" +msgstr "Chụp snapshot…" + +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "" + +#: qt/app.py:663 +#, fuzzy +msgid "Shows the message about this Release Candidate again." +msgstr "Hiển thị lại thông báo về việc gỡ bỏ EncFS." + +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "" + +#: qt/app.py:705 +msgid "&Backup" +msgstr "&Sao lưu" + +#: qt/app.py:717 +msgid "&Restore" +msgstr "&Khôi phục" + +#: qt/app.py:723 +msgid "&Help" +msgstr "T&rợ giúp" + +#: qt/app.py:774 +msgid "Systray Icon" +msgstr "" + +#: qt/app.py:780 +msgid "Automatic" +msgstr "" + +#: qt/app.py:784 +msgid "Light icon" +msgstr "" + +#: qt/app.py:785 +msgid "Dark icon" +msgstr "" + +#: qt/app.py:808 +msgid "Icons only" +msgstr "" + +#: qt/app.py:811 +msgid "Text only" +msgstr "" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." +msgstr "" +"Thư mục này không tồn tại\n" +"trong bản snapshot đang được chọn." + +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." +msgstr "" +"Thư mục này không tồn tại\n" +"trong bản snapshot đang được chọn." + +#: qt/app.py:995 +#, fuzzy +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." +msgstr "" +"Nếu bạn đóng cửa sổ này, Back In Time sẽ không thể tắt máy của bạn khi quá " +"trình chụp snapshot hoàn tất." + +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "" + +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "Xong, không cần sao lưu" + +#: qt/app.py:1260 +msgid "Working:" +msgstr "Đang chạy:" + +#: qt/app.py:1267 +msgid "Working" +msgstr "Đang chạy" + +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "Lỗi" + +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "Đã gửi:" + +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "Tốc độ:" + +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "" + +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "&Sao lưu" + +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "Khôi phục {path}" + +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "Khôi phục {path} đến …" + +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"Xin chào\n" +"Bạn cũng đã sử dụng Back In Time bằng {language} được vài lần rồi.\n" +"Bản dịch sang {language} của phiên bản Back In Time mà bạn cài đặt đã {perc} hoàn thành. Dù với bất cứ trình độ hiểu biết công nghệ nào, bạn cũng có thể đóng góp bản dịch và cũng là đóng góp cho chính Back In Time.\n" +"Xin hãy ghé thăm {translation_platform_url} nếu bạn muốn đóng góp. Về trợ giúp và giải đáp thắc mặc, xin hãy truy cập {back_in_time_project_website}.\n" +"Chúng tôi xin thứ lỗi về sự gián đoạn này, và thông báo này sẽ không xuất hiện nữa. Hộp thoại này lúc nào cũng có thể được tìm thấy trong menu trợ giúp.\n" +"Nhóm phát triển Back In Time" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "nền tảng phiên dịch" + +#: qt/app.py:1658 +msgid "Website" +msgstr "Trang web" + +#: qt/app.py:1672 +msgid "Your translation" +msgstr "Bản dịch của bạn" + +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "" + +#: qt/app.py:1714 +#, python-brace-format +msgid "Email to {link_and_label}." +msgstr "" + +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "" + +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "" + +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "" + +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "" + +#: qt/app.py:1731 +#, python-brace-format +msgid "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." +msgstr "" + +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "" + +#: qt/app.py:1875 +#, fuzzy, python-brace-format +msgid "" +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." +msgstr "" +"CẢNH BÁO: Xóa các tập tin trong thư mục root của hệ thống tập tin có thể làm" +" hỏng hệ thống của bạn!" + +#: qt/app.py:2107 +#, fuzzy +msgid "Backup name" +msgstr "&Sao lưu" + +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "" +"Thiết đặt ngôn ngữ chỉ có tác dụng sau khi khởi động lại Back In Time." + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" +msgstr "" + +#: qt/backintime-qt-root.desktop:23 +msgid "" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" +msgstr "" + +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "Đừng xóa các bản snapshot đã được đổi tên." + +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" +msgstr "" + +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "Thắc mắc" + +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format +msgid "" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." +msgstr "" +"Tạo thêm bản sao lưu với {suffix} ở cuối tên\n" +"trước khi ghi đè hoặc xóa các phần tử cục bộ." + +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, fuzzy, python-brace-format +msgid "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" +msgstr "" +"Những phiên bản mới hơn của các tập tin sẽ được đổi tên với {suffix} ở cuối " +"tên trước khi khôi phục. Nếu bạn không cần chúng nữa, bạn có thể xóa chúng " +"với {command}:" + +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format +msgid "" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." +msgstr "" +"Chỉ khôi phục những tập tin không tồn tại hoặc\n" +"có phiên bản mới hơn so với trong thư mục đích.\n" +"Sử dụng lệnh \"rsync --update\"." + +#: qt/confirmrestoredialog.py:133 +#, fuzzy +msgid "Remove newer elements in original directory." +msgstr "Xóa các tập tin có phiên bản mới hơn trong thư mục gốc." + +#: qt/confirmrestoredialog.py:135 +#, fuzzy +msgid "" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "" +"Khôi phục các tập tin hay thư mục được chọn đến thư mục đích và\n" +"xóa các tập tin hay thư mục không có trong snapshot.\n" +"Hãy cực kỳ cẩn thận bởi vì việc này sẽ \n" +"xóa bỏ các tệp tin và thư mục \n" +"đã bị loại trừ trong khi chụp snapshot." + +#: qt/confirmrestoredialog.py:147 +#, fuzzy +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "" +"Bạn có thật sự muốn khôi phục các phần tử này vào thư mục mới\n" +" {path}?" + +#: qt/confirmrestoredialog.py:157 +#, fuzzy +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "Bạn có thật sự muốn khôi phục các phần tử này?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "" + +#: qt/filedialog.py:87 +#, fuzzy +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "Bao gồm các tập tin và thư mục" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "Thêm vào Bao gồm" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "Thêm vào Loại trừ" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "Thiết đặt ngôn ngữ" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "Đã dịch: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "Mặc định hệ thống" + +#: qt/languagedialog.py:142 +#, fuzzy +msgid "Use operating system's language." +msgstr "Dùng ngôn ngữ của hệ điều hành." + +#: qt/logviewdialog.py:63 +#, fuzzy +msgid "Backup Log View" +msgstr "Xem nhật ký được lưu lần cuối cùng" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "Xem nhật ký được lưu lần cuối cùng" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "Hồ sơ:" + +#: qt/logviewdialog.py:86 +#, fuzzy +msgid "Backups:" +msgstr "&Sao lưu" + +#: qt/logviewdialog.py:93 +#, fuzzy +msgid "Filter:" +msgstr "Bộ lọc" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] Lỗi, [I] Thông tin, [C] Thay đổi" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "giải mã đường dẫn" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "Tất cả" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "Có thay đổi" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "Có lỗi" + +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +#, fuzzy +msgid "Information" +msgid_plural "Information" +msgstr[0] "Thông tin" + +#: qt/logviewdialog.py:135 +#, fuzzy +msgid "rsync transfer failures (experimental)" +msgstr "Truyền rsync thất bại (thử nghiệm)" + +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "Quản lý hồ sơ" + +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "Sửa" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "Thêm" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "Gỡ bỏ" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&Tổng quan" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "&Bao gồm" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&Ngoại trừ" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "Tùy &chọn" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "Tùy chọn chuyên &gia" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "Khôi phục cấu hình" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "Hồ sơ mới" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "Đổi tên hồ sơ" + +#: qt/manageprofiles/__init__.py:215 +#, fuzzy, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "Bạn có chắc chắn muốn xóa hồ sơ \"{name}\" ?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:43 +msgid "" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." +msgstr "" + +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "Tạm dừng quá trình chụp snapshot" + +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "Loại trừ tập tin" + +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "Các thư mục sao lưu" + +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "Không thể xóa thư mục" + +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "Hiện tập tin ẩn" + +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "Dừng hẳn quá trình chụp snapshot" + +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "Loại trừ thư mục" + +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." +msgstr "" + +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "mặc định" + +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" +msgstr "Lịch trình" + +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" +msgstr "ngày thường:" + +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" +msgstr "giờ:" + +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:93 +#, fuzzy +msgid "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "" +"Chạy Back In Time ngay khi ổ cứng được kết nối (chỉ một lần mỗi X ngày).\n" +"Bạn sẽ bị yêu cầu nhập mật khẩu sudo." + +#: qt/manageprofiles/schedulewidget.py:98 +msgid "" +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." +msgstr "" +"Chạy Back In Time theo định kỳ. Lựa chọn này hữu ích khi máy tính không được" +" mở thường xuyên." + +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" +msgstr "Mỗi:" + +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:120 +msgid "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "" + +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "Đang tắt" + +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "Mỗi lần khởi động máy" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, fuzzy, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "Mỗi {n} phút" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, fuzzy, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "Mỗi giờ" + +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "Theo giờ tùy chỉnh" + +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "Hàng ngày" + +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "Theo định kỳ (anacron)" + +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "Khi ổ cứng kết nối thành công (udev)" + +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "Hàng tuần" + +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "Hàng tháng" + +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "Hàng năm" + +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "Giờ" + +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "Ngày" + +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "Tuần" + +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "Tháng" + +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." +msgstr "" +"Giờ tùy chỉnh phải là các số giờ được ngăn cách bởi dấu phẩy (vd: " +"8,12,18,23) hoặc theo định dạng */3 để sao lưu sau mỗi 3 tiếng." + +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "" +msgstr "<选择另一个文件...>" + +#: qt/manageprofiles/sshkeyselector.py:65 +msgid "Choose an existing private key file from somewhere else." +msgstr "从其他位置选择现有的私钥文件。" + +#: qt/manageprofiles/sshkeyselector.py:71 +msgid "" +msgstr "<生成新的密钥对...>" + +#: qt/manageprofiles/sshkeyselector.py:72 +msgid "Create a new SSH key without passphrase." +msgstr "创建一个没有密码的新 SSH 密钥。" + +#: qt/manageprofiles/sshkeyselector.py:92 +#, python-brace-format +msgid "Full path: {path}" +msgstr "完整路径:{path}" + +#: qt/manageprofiles/sshkeyselector.py:205 +msgid "Private key:" +msgstr "私钥:" -#: ../../qt/app.py:1083 -#, python-format +#: qt/manageprofiles/sshkeyselector.py:210 +msgid "Use system SSH configuration" +msgstr "使用系统 SSH 配置" + +#: qt/manageprofiles/sshkeyselector.py:212 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" -msgstr "" +"Leaves the key file unselected. SSH connections will rely on the system’s " +"existing client configuration (e.g., ~/.ssh/config)." +msgstr "保留密钥文件未选中状态。SSH 连接将依赖于系统现有的客户端配置(例如 ~/.ssh/config)。" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:47 +msgid "SSH Proxy" +msgstr "SSH 代理" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/manageprofiles/sshproxywidget.py:54 qt/manageprofiles/tab_general.py:117 +#: qt/manageprofiles/tab_general.py:238 +msgid "Host:" +msgstr "主机:" + +#: qt/manageprofiles/sshproxywidget.py:58 qt/manageprofiles/tab_general.py:122 +msgid "Port:" +msgstr "端口:" + +#: qt/manageprofiles/sshproxywidget.py:62 qt/manageprofiles/tab_general.py:127 +#: qt/manageprofiles/tab_general.py:244 +msgid "User:" +msgstr "用户:" -#: ../../qt/app.py:1101 +#: qt/manageprofiles/sshproxywidget.py:71 msgid "" -"Are you sure you want to remove all newer files in your original folder?" +"Connect to the target host via this proxy (also known as a jump host). See " +"\"-J\" in the \"ssh\" command documentation or \"ProxyJump\" in " +"\"ssh_config\" man page for details." msgstr "" +"通过此代理(又称跳转主机)连接到目标主机。详情请参阅“ssh”命令文档中的“-J”或“ssh_config”手册页中的“ProxyJump”。" -#: ../../qt/app.py:1105 +#: qt/manageprofiles/tab_exclude.py:62 +#, python-brace-format msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" +"{BOLD}Info{ENDBOLD}: In 'SSH encrypted' mode, only single or double " +"asterisks are functional (e.g. {example2}). Other types of wildcards and " +"patterns will be ignored (e.g. {example1}). Filenames are unpredictable in " +"this mode due to encryption by EncFS." msgstr "" +"{BOLD}信息{ENDBOLD}:在“SSH 加密”模式下,只有单星号或双星号有效(例如 {example2})。其他类型的通配符和模式将被忽略(例如" +" {example1})。由于 EncFS 加密,此模式下文件名不可预测。" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "查看当前磁盘内容" +#: qt/manageprofiles/tab_exclude.py:85 +msgid "Exclude patterns, files or directories" +msgstr "排除模式、文件或目录" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "快照:%s" +#: qt/manageprofiles/tab_exclude.py:102 +msgid "Add pattern" +msgstr "添加模式" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "查看快照 %s" +#: qt/manageprofiles/tab_exclude.py:107 qt/manageprofiles/tab_include.py:68 +msgid "Add files" +msgstr "添加文件" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "还原 '%s'" +#: qt/manageprofiles/tab_exclude.py:112 qt/manageprofiles/tab_include.py:73 +msgid "Add directories" +msgstr "添加目录" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "还原 '%s' 至 ..." +#: qt/manageprofiles/tab_exclude.py:118 +msgid "Suggestions" +msgstr "建议" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:120 +msgid "Select from common used items to add to exclude list." +msgstr "从常用项目中选择添加到排除列表中。" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:134 +msgid "Exclude files bigger than:" +msgstr "排除大于设定值的文件:" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:138 +#, python-brace-format +msgid "Exclude files bigger than value in {size_unit}." +msgstr "排除大于设定值的文件,单位 {size_unit}。" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" +#: qt/manageprofiles/tab_exclude.py:140 +msgid "" +"With 'Full rsync mode' disabled, this setting affects only newly created " +"files, as rsync treats it as transfer option rather than an exclusion rule. " +"Consequently, large files that have already been backed up will remain in " +"backups even if they are modified." msgstr "" +"禁用“完全 rsync 模式”后,此设置仅影响新创建的文件,因为 rsync " +"将其视为传输选项而非排除规则。因此,已备份的大文件即使被修改,也会保留在备份中。" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +#: qt/manageprofiles/tab_exclude.py:265 +msgid "Exclude pattern" +msgstr "排除模式" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "配置:" +#: qt/manageprofiles/tab_exclude.py:267 +msgid "Enter an exclude pattern:" +msgstr "输入排除模式:" -#: ../../qt/logviewdialog.py:89 -msgid "Filter:" -msgstr "过滤器" +#: qt/manageprofiles/tab_exclude.py:272 +#, python-brace-format +msgid "For help, see the rsync man page section {link}." +msgstr "如需帮助,请参阅 rsync 手册页的 {link} 部分。" -#: ../../qt/logviewdialog.py:95 ../../qt/settingsdialog.py:661 -msgid "All" -msgstr "所有的" +#: qt/manageprofiles/tab_exclude.py:275 +msgid "Open rsync man page" +msgstr "打开 rsync 手册页" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:98 -#: ../../qt/settingsdialog.py:659 -msgid "Errors" -msgstr "错误" +#: qt/manageprofiles/tab_exclude.py:303 +msgid "Exclude files" +msgstr "排除文件" -#: ../../qt/logviewdialog.py:96 ../../qt/logviewdialog.py:99 -msgid "Changes" -msgstr "变更" +#: qt/manageprofiles/tab_exclude.py:316 +msgid "Exclude directories" +msgstr "排除目录" -#: ../../qt/logviewdialog.py:100 -msgid "Informations" -msgstr "提示信息" +#: qt/manageprofiles/tab_exclude.py:401 +msgid "" +"Disabled because this pattern is not functional in mode 'SSH encrypted'." +msgstr "已禁用,因为此模式在“SSH 加密”模式下不起作用。" -#: ../../qt/logviewdialog.py:110 -msgid "[E] Error, [I] Information, [C] Change" -msgstr "[E] 错误,[I] 通知,[C] 变更" +#: qt/manageprofiles/tab_expert_options.py:45 +msgid "" +"These options are for advanced configurations. Modify only if fully aware of" +" their implications." +msgstr "这些选项适用于高级配置。只有完全意识到其影响的情况下才修改。" + +#: qt/manageprofiles/tab_expert_options.py:54 +#: qt/manageprofiles/tab_expert_options.py:74 +#: qt/manageprofiles/tab_expert_options.py:99 +#, python-brace-format +msgid "Run 'rsync' with '{cmd}':" +msgstr "使用“{cmd}”运行“rsync”:" + +#: qt/manageprofiles/tab_expert_options.py:61 +#: qt/manageprofiles/tab_expert_options.py:80 +msgid "as cron job" +msgstr "作为 cron 作业" -#: ../../qt/logviewdialog.py:113 ../../qt/qtsystrayicon.py:91 -msgid "decode paths" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:67 +#: qt/manageprofiles/tab_expert_options.py:92 +#: qt/manageprofiles/tab_expert_options.py:123 +msgid "on remote host" +msgstr "在远程主机上" -#: ../../qt/logviewdialog.py:171 -msgid "Copy" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:86 +msgid "when taking a manual backup" +msgstr "进行手动备份时" -#: ../../qt/logviewdialog.py:179 -msgid "Decode" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:110 +msgid "Please install 'nocache' to enable this option." +msgstr "请安装“nocache”以启用此选项。" -#: ../../qt/logviewdialog.py:195 -msgid "Do you want to exclude this?" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:116 +msgid "on local machine" +msgstr "在本地机器上" -#: ../../qt/messagebox.py:58 -msgid "Error" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:130 +msgid "Redirect stdout to /dev/null in cronjobs." +msgstr "在定时任务中将标准输出重定向到 /dev/null。" -#: ../../qt/messagebox.py:64 ../../qt/messagebox.py:70 -msgid "Question" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:136 +msgid "" +"Cron will automatically send an email with attached output of cronjobs if an" +" MTA is installed." +msgstr "如果安装了 MTA,Cron 将自动发送一封附有 cronjobs 输出的电子邮件。" -#: ../../qt/qtsystrayicon.py:98 -msgid "Start BackInTime" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:142 +msgid "Redirect stderr to /dev/null in cronjobs." +msgstr "在定时任务中将标准错误重定向到 /dev/null。" -#: ../../qt/qtsystrayicon.py:157 -msgid "Working..." -msgstr "正在进行..." +#: qt/manageprofiles/tab_expert_options.py:148 +msgid "" +"Cron will automatically send an email with attached errors of cronjobs if an" +" MTA is installed." +msgstr "如果安装了 MTA,Cron 将自动发送一封附有 cronjobs 错误的电子邮件。" -#: ../../qt/qttools.py:220 -msgid "Today" -msgstr "今天" +#: qt/manageprofiles/tab_expert_options.py:158 +msgid "KB/sec" +msgstr "KB/秒" -#: ../../qt/qttools.py:224 -msgid "Yesterday" -msgstr "昨天" +#: qt/manageprofiles/tab_expert_options.py:163 +msgid "Limit rsync bandwidth usage:" +msgstr "限制 rsync 带宽使用:" -#: ../../qt/qttools.py:229 -msgid "This week" -msgstr "本周" +#: qt/manageprofiles/tab_expert_options.py:204 +msgid "Preserve ACL" +msgstr "保留 ACL" -#: ../../qt/qttools.py:233 -msgid "Last week" -msgstr "上周" +#: qt/manageprofiles/tab_expert_options.py:222 +msgid "Preserve extended attributes (xattr)" +msgstr "保留扩展属性(xattr)" -#: ../../qt/qttools.py:352 -msgid "This is NOT a snapshot but a live view of your local files" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:249 +msgid "Restrict to one file system" +msgstr "限制为一个文件系统" -#: ../../qt/qttools.py:354 -#, python-format -msgid "Last check %s" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:267 +#, python-brace-format +msgid "Options must be quoted e.g. {example}." +msgstr "选项必须使用引号,例如:{example}。" -#: ../../qt/restoredialog.py:64 -msgid "Show full Log" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:276 +msgid "Paste additional options to rsync" +msgstr "粘贴附加选项到 rsync" -#: ../../qt/settingsdialog.py:76 -msgid "Edit" -msgstr "编辑" +#: qt/manageprofiles/tab_expert_options.py:286 +msgid "Prefix to run before every command on remote host." +msgstr "在远程主机上每个命令之前运行的前缀。" -#: ../../qt/settingsdialog.py:81 -msgid "Modify for Full System Backup" +#: qt/manageprofiles/tab_expert_options.py:287 +#, python-brace-format +msgid "" +"Variables need to be escaped with \\$FOO. This doesn't touch rsync. So to " +"add a prefix for rsync use \"{example_value}\" with {rsync_options_value}." msgstr "" +"变量需要使用 \\$FOO 进行转义。因为这不涉及 rsync,所以要为 rsync " +"添加前缀请使用{example_value}和{rsync_options_value}。" -#: ../../qt/settingsdialog.py:87 ../../qt/settingsdialog.py:464 -msgid "Add" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:293 +msgid "default" +msgstr "默认" -#: ../../qt/settingsdialog.py:91 ../../qt/settingsdialog.py:425 -#: ../../qt/settingsdialog.py:480 -msgid "Remove" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:298 +msgid "Add prefix to SSH commands" +msgstr "为 SSH 命令添加前缀" + +#: qt/manageprofiles/tab_expert_options.py:308 +msgid "Check if remote host is online" +msgstr "检查远程主机是否在线" -#: ../../qt/settingsdialog.py:106 -msgid "General" -msgstr "常规" +#: qt/manageprofiles/tab_expert_options.py:311 +msgid "" +"Warning: If disabled and the remote host is not available, this could lead " +"to some weird errors." +msgstr "警告:如果禁用且远程主机不可用,这可能会导致一些奇怪的错误。" -#: ../../qt/settingsdialog.py:116 ../../qt/settingsdialog.py:1970 -msgid "Mode:" -msgstr "模式:" +#: qt/manageprofiles/tab_expert_options.py:315 +msgid "Check if remote host supports all necessary commands." +msgstr "检查远程主机是否支持所有必要的命令。" -#: ../../qt/settingsdialog.py:129 -#, python-format +#: qt/manageprofiles/tab_expert_options.py:318 msgid "" -"Warning: %(app)s uses EncFS for encryption. A recent security audit " -"revealed several possible attack vectors for this. Please take a look at 'A " -"NOTE ON SECURITY' in 'man backintime'." -msgstr "" +"Warning: If disabled and the remote host does not support all necessary " +"commands, this could lead to some weird errors." +msgstr "警告:如果禁用且远程主机不支持所有必要的命令,这可能会导致一些奇怪的错误。" -#: ../../qt/settingsdialog.py:139 ../../qt/settingsdialog.py:1610 -msgid "Where to save snapshots" -msgstr "何处保存快照" +#: qt/manageprofiles/tab_expert_options.py:332 +msgid "(default: {})" +msgstr "(默认:{})" -#: ../../qt/settingsdialog.py:155 -msgid "Folder" -msgstr "" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "disabled" +msgstr "已禁用" -#: ../../qt/settingsdialog.py:163 -msgid "SSH Settings" -msgstr "SSH 设置" +#: qt/manageprofiles/tab_expert_options.py:333 +msgid "enabled" +msgstr "已启用" -#: ../../qt/settingsdialog.py:175 ../../qt/settingsdialog.py:282 -msgid "Host:" -msgstr "主机:" +#: qt/manageprofiles/tab_general.py:67 qt/restoreconfigdialog.py:345 +msgid "Mode:" +msgstr "模式:" -#: ../../qt/settingsdialog.py:180 -msgid "Port:" -msgstr "端口:" +#: qt/manageprofiles/tab_general.py:79 qt/manageprofiles/tab_general.py:394 +#: qt/manageprofiles/tab_general.py:686 +msgid "Where to save backups" +msgstr "保存备份的路径" -#: ../../qt/settingsdialog.py:185 ../../qt/settingsdialog.py:288 -msgid "User:" -msgstr "用户名:" +#: qt/manageprofiles/tab_general.py:105 +msgid "SSH Settings" +msgstr "SSH 设置" -#: ../../qt/settingsdialog.py:190 +#: qt/manageprofiles/tab_general.py:132 msgid "Path:" msgstr "路径:" -#: ../../qt/settingsdialog.py:196 -msgid "Cipher:" -msgstr "" - -#: ../../qt/settingsdialog.py:202 -msgid "Private Key:" -msgstr "私钥:" - -#: ../../qt/settingsdialog.py:211 -msgid "Key File" -msgstr "" - -#: ../../qt/settingsdialog.py:219 -msgid "Create a new SSH key without Password." -msgstr "" +#: qt/manageprofiles/tab_general.py:139 +msgid "Key file:" +msgstr "密钥文件:" -#: ../../qt/settingsdialog.py:234 ../../qt/settingsdialog.py:243 -#: ../../qt/settingsdialog.py:249 +#: qt/manageprofiles/tab_general.py:176 qt/manageprofiles/tab_general.py:184 +#: qt/manageprofiles/tab_general.py:189 msgid "Password" msgstr "密码" -#: ../../qt/settingsdialog.py:255 +#: qt/manageprofiles/tab_general.py:206 msgid "Save Password to Keyring" msgstr "保存密码至密钥环" -#: ../../qt/settingsdialog.py:258 +#: qt/manageprofiles/tab_general.py:210 msgid "Cache Password for Cron (Security issue: root can read password)" -msgstr "" +msgstr "Cron 的缓存密码(安全问题:root 可以读取密码)" -#: ../../qt/settingsdialog.py:270 +#: qt/manageprofiles/tab_general.py:226 msgid "Advanced" msgstr "高级" -#: ../../qt/settingsdialog.py:300 ../../qt/settingsdialog.py:1690 -msgid "Full snapshot path: " -msgstr "" - -#: ../../qt/settingsdialog.py:307 -msgid "Schedule" -msgstr "计划任务" - -#: ../../qt/settingsdialog.py:317 -msgid "Day:" -msgstr "日:" +#: qt/manageprofiles/tab_general.py:256 qt/manageprofiles/tab_general.py:803 +msgid "Full backup path:" +msgstr "完整备份路径:" -#: ../../qt/settingsdialog.py:328 -msgid "Weekday:" -msgstr "工作日:" +#: qt/manageprofiles/tab_general.py:264 +msgid "" +"Scheduling is disabled because no cron installation was found. Please " +"install cron to enable scheduled backups." +msgstr "由于未找到 cron 安装,因此已禁用计划任务功能。请安装 cron 以启用计划备份。" -#: ../../qt/settingsdialog.py:339 -msgid "Hour:" -msgstr "小时:" +#: qt/manageprofiles/tab_general.py:393 +msgid "The backup destination path cannot be empty." +msgstr "备份目标路径不能为空。" -#: ../../qt/settingsdialog.py:350 -msgid "Hours:" -msgstr "小时:" +#: qt/manageprofiles/tab_general.py:404 +msgid "The encryption password cannot be empty." +msgstr "加密密码不能为空。" -#: ../../qt/settingsdialog.py:359 +#: qt/manageprofiles/tab_general.py:553 msgid "" -"Run Back In Time repeatedly. This is useful if the computer is not running " -"regularly." -msgstr "" - -#: ../../qt/settingsdialog.py:364 -msgid "Every:" -msgstr "" +"An error occurred while attempting to log in to the remote host. The " +"following error message was returned:" +msgstr "尝试登录到远程主机时出错。返回以下错误消息:" -#: ../../qt/settingsdialog.py:382 +#: qt/manageprofiles/tab_general.py:557 msgid "" -"Run Back In Time as soon as the drive is connected (only once every X " -"days).\n" -"You will be prompted for your sudo password." -msgstr "" +"To enable password-less login, the public SSH key can be copied to the " +"remote host." +msgstr "要启用免密码登录,可以将 SSH 公钥复制到远程主机。" -#: ../../qt/settingsdialog.py:395 -msgid "Include" -msgstr "包含" +#: qt/manageprofiles/tab_general.py:560 +msgid "Proceed with copying the SSH key?" +msgstr "是否继续复制 SSH 密钥?" -#: ../../qt/settingsdialog.py:401 -msgid "Include files and folders" -msgstr "包含文件和文件夹" +#: qt/manageprofiles/tab_general.py:585 +msgid "" +"The public SSH key could not be copied. This may be due to a connection or " +"permission issue." +msgstr "无法复制 SSH 公钥。这可能是由于连接或权限问题。" + +#: qt/manageprofiles/tab_general.py:606 +#, python-brace-format +msgid "The authenticity of host {host} can't be established." +msgstr "无法确定主机 {host} 的真实性。" + +#: qt/manageprofiles/tab_general.py:609 +#, python-brace-format +msgid "{keytype} key fingerprint is:" +msgstr "{keytype} 密钥指纹为:" + +#: qt/manageprofiles/tab_general.py:613 +msgid "Please verify this fingerprint. Add it to the \"known_hosts\" file?" +msgstr "请验证此指纹。是否将其添加到“known_hosts”文件中?" + +#: qt/manageprofiles/tab_general.py:709 +msgid "Really change the backup directory?" +msgstr "是否确定要更改备份目录?" + +#: qt/manageprofiles/tab_general.py:725 +msgid "The selected backup destination is not empty." +msgstr "所选备份目标不为空。" + +#: qt/manageprofiles/tab_general.py:727 +msgid "It must be empty to use encryption." +msgstr "使用加密时必须为空。" + +#: qt/manageprofiles/tab_general.py:756 +msgid "Invalid file: Not a private SSH key" +msgstr "文件无效:不是 SSH 私钥" + +#: qt/manageprofiles/tab_general.py:757 +#, python-brace-format +msgid "" +"The selected file ({path}) is a public SSH key. Please choose the " +"corresponding private key file instead (without \".pub\")." +msgstr "所选文件({path})是 SSH 公钥。请选择相应的私钥文件(不含“.pub”扩展名)。" -#: ../../qt/settingsdialog.py:417 ../../qt/settingsdialog.py:468 -msgid "Add file" -msgstr "添加文件" +#: qt/manageprofiles/tab_general.py:781 +#, python-brace-format +msgid "" +"The file {path} already exists. Cannot create a new SSH key with that name." +msgstr "文件 {path} 已存在。无法使用该名称创建新的 SSH 密钥。" -#: ../../qt/settingsdialog.py:421 ../../qt/settingsdialog.py:472 -msgid "Add folder" -msgstr "添加文件夹" +#: qt/manageprofiles/tab_general.py:791 +#, python-brace-format +msgid "Failed to create new SSH key in {path}." +msgstr "无法在 {path} 创建新 SSH 密钥。" -#: ../../qt/settingsdialog.py:431 -msgid "Exclude" -msgstr "排除" +#: qt/manageprofiles/tab_include.py:48 +msgid "Include files and directories" +msgstr "包括文件和目录" -#: ../../qt/settingsdialog.py:434 +#: qt/manageprofiles/tab_include.py:168 +#, python-brace-format msgid "" -"Warning: Wildcards ('foo*', '[fF]oo', 'fo?') will be ignored with " -"mode 'SSH encrypted'.\n" -"Only separate asterisk are allowed ('foo/*', 'foo/**/bar')" -msgstr "" +"\"{path}\" is a symlink. The linked target will not be backed up until it is" +" included, too." +msgstr "“{path}”是符号链接,链接的目标也要包括在内才会备份。" -#: ../../qt/settingsdialog.py:441 -msgid "Exclude patterns, files or folders" -msgstr "不包含模式,文件或文件夹" +#: qt/manageprofiles/tab_include.py:172 +msgid "Include the symlink's target instead?" +msgstr "是否改为包括符号链接目标?" -#: ../../qt/settingsdialog.py:454 -msgid "Highly recommended:" -msgstr "强烈推荐:" +#: qt/manageprofiles/tab_include.py:180 +msgid "Include files" +msgstr "包括文件" -#: ../../qt/settingsdialog.py:476 -msgid "Add default" -msgstr "" +#: qt/manageprofiles/tab_include.py:199 +msgid "Include directories" +msgstr "包括目录" -#: ../../qt/settingsdialog.py:487 -msgid "Exclude files bigger than: " -msgstr "" +#: qt/manageprofiles/tab_options.py:39 +msgid "Enable notifications" +msgstr "启用通知" -#: ../../qt/settingsdialog.py:488 -#, python-format -msgid "" -"Exclude files bigger than value in %(prefix)s.\n" -"With 'Full rsync mode' disabled this will only affect new files\n" -"because for rsync this is a transfer option, not an exclude option.\n" -"So big files that has been backed up before will remain in snapshots\n" -"even if they had changed." -msgstr "" +#: qt/manageprofiles/tab_options.py:43 +msgid "Disable backups when on battery" +msgstr "电池供电时禁用备份" -#: ../../qt/settingsdialog.py:506 -msgid "Auto-remove" -msgstr "自动移除" +#: qt/manageprofiles/tab_options.py:49 +msgid "Power status not available from system" +msgstr "无法从系统中获取电源状态" -#: ../../qt/settingsdialog.py:513 -msgid "Older than:" -msgstr "早于:" +#: qt/manageprofiles/tab_options.py:52 +msgid "Run only one backup at a time" +msgstr "一次仅运行一个备份" -#: ../../qt/settingsdialog.py:528 -msgid "If free space is less than:" -msgstr "如果磁盘可用空间少于:" +#: qt/manageprofiles/tab_options.py:56 +msgid "" +"Other backups will be blocked until the current backup is completed. This is" +" a global setting, meaning it will affect all profiles for this user. " +"However, it must also be activated for all other users." +msgstr "在当前备份完成前会阻止其他备份。这是全局设置,意味着将影响此用户的所有配置。但是,还必须为所有其他用户启用该设置。" -#: ../../qt/settingsdialog.py:541 -msgid "If free inodes is less than:" -msgstr "" +#: qt/manageprofiles/tab_options.py:63 +msgid "Backup replaced files on restore" +msgstr "恢复时备份替换的文件" -#: ../../qt/settingsdialog.py:564 -msgid "Run in background on remote Host." -msgstr "" +#: qt/manageprofiles/tab_options.py:78 +msgid "Continue on errors (keep incomplete backups)" +msgstr "出错时继续(保留不完整的备份)" -#: ../../qt/settingsdialog.py:567 -msgid "Keep all snapshots for the last" -msgstr "保留最新的所有快照" +#: qt/manageprofiles/tab_options.py:82 +msgid "Use checksum to detect changes" +msgstr "使用校验和检测更改" -#: ../../qt/settingsdialog.py:571 ../../qt/settingsdialog.py:577 -msgid "day(s)" -msgstr "天" +#: qt/manageprofiles/tab_options.py:86 +msgid "Create a new backup whether there were changes or not." +msgstr "无论是否有更改,都创建新备份。" -#: ../../qt/settingsdialog.py:573 -msgid "Keep one snapshot per day for the last" -msgstr "每天保留一个最新的快照" +#: qt/manageprofiles/tab_options.py:95 +msgid "Warn if the free disk space falls below" +msgstr "发出警告,当可用磁盘空间低于" -#: ../../qt/settingsdialog.py:579 -msgid "Keep one snapshot per week for the last" -msgstr "每周保留一个最新的快照" +#: qt/manageprofiles/tab_options.py:101 +msgid "" +"Shows a warning when free space on the backup destination disk is less than " +"the specified value." +msgstr "当备份目标磁盘上的可用空间小于指定值时显示警告。" -#: ../../qt/settingsdialog.py:583 -msgid "weeks(s)" -msgstr "周" +#: qt/manageprofiles/tab_options.py:103 +msgid "" +"If the Remove & Retention policy is enabled and old backups are removed " +"based on available free space, this value cannot be lower than the value set" +" in the policy." +msgstr "如果启用了“移除和保留”策略,并根据可用空间移除旧备份,则此值不能低于策略中设置的值。" -#: ../../qt/settingsdialog.py:585 -msgid "Keep one snapshot per month for the last" -msgstr "每月保留一个最新的快照" +#: qt/manageprofiles/tab_options.py:122 +msgid "Log Level:" +msgstr "日志级别:" -#: ../../qt/settingsdialog.py:589 -msgid "month(s)" -msgstr "月" +#: qt/manageprofiles/tab_options.py:185 +msgid "None" +msgstr "无" -#: ../../qt/settingsdialog.py:591 -msgid "Keep one snapshot per year for all years" -msgstr "每年保留一个快照" +#: qt/manageprofiles/tab_remove_retention.py:206 +msgid "user manual" +msgstr "用户手册" -#: ../../qt/settingsdialog.py:598 -msgid "Don't remove named snapshots" -msgstr "不要移除已命名的快照" +#: qt/manageprofiles/tab_remove_retention.py:208 +#, python-brace-format +msgid "" +"The following rules are processed from top to bottom. Later rules override " +"earlier ones. See the {manual_link} for details and examples." +msgstr "以下规则按从上到下的顺序处理。后出现的规则会覆盖先前的规则。详情及示例请参阅{manual_link}。" -#: ../../qt/settingsdialog.py:610 -msgid "Options" -msgstr "选项" +#: qt/manageprofiles/tab_remove_retention.py:223 +msgid "Open user manual in browser." +msgstr "在浏览器中打开用户手册。" -#: ../../qt/settingsdialog.py:615 -msgid "Enable notifications" -msgstr "启用通知" +#: qt/manageprofiles/tab_remove_retention.py:237 +msgid "Keep the most recent backup." +msgstr "保留最新备份。" -#: ../../qt/settingsdialog.py:618 -msgid "Disable snapshots when on battery" -msgstr "使用电池时禁用快照" +#: qt/manageprofiles/tab_remove_retention.py:241 +msgid "The most up-to-date backup is kept under all circumstances." +msgstr "在任何情况下都会保留最新的备份。" -#: ../../qt/settingsdialog.py:621 -msgid "Power status not available from system" -msgstr "无法从系统中获取电源状态" +#: qt/manageprofiles/tab_remove_retention.py:243 +msgid "That behavior cannot be changed." +msgstr "这种行为无法更改。" -#: ../../qt/settingsdialog.py:624 -msgid "Run only one snapshot at a time" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:255 +msgid "Keep named backups." +msgstr "保留已命名的备份。" -#: ../../qt/settingsdialog.py:625 +#: qt/manageprofiles/tab_remove_retention.py:258 msgid "" -"Other snapshots will be blocked until the current snapshot is done.\n" -"This is a global option. So it will effect all profiles for this user.\n" -"But you need to activate this for all other users, too." -msgstr "" - -#: ../../qt/settingsdialog.py:630 -msgid "Backup replaced files on restore" -msgstr "" +"Backups that have been given a name, in addition to the usual timestamp, " +"will be retained under all circumstances and will not be removed." +msgstr "除了通常的时间戳外,已命名的备份在任何情况下都将保留,不会被移除。" -#: ../../qt/settingsdialog.py:640 -msgid "Continue on errors (keep incomplete snapshots)" -msgstr "忽略错误并继续(保留不完整的快照)" - -#: ../../qt/settingsdialog.py:646 -msgid "Take a new snapshot regardless of there were changes or not." -msgstr "" - -#: ../../qt/settingsdialog.py:653 -msgid "Log Level:" -msgstr "日志级别" +#: qt/manageprofiles/tab_remove_retention.py:273 +msgid "Year(s)" +msgstr "年" -#: ../../qt/settingsdialog.py:658 -msgid "None" -msgstr "空" +#: qt/manageprofiles/tab_remove_retention.py:278 +msgid "Remove backups older than" +msgstr "移除多久前的备份" -#: ../../qt/settingsdialog.py:660 -msgid "Changes & Errors" -msgstr "变更 & 错误" +#: qt/manageprofiles/tab_remove_retention.py:284 +msgid "Full days. Current day is ignored." +msgstr "整天数,当日忽略不计。" -#: ../../qt/settingsdialog.py:676 -msgid "Change these options only if you really know what you are doing !" -msgstr "除非您知道自己在干什么,否则不要变更这些选项!" +#: qt/manageprofiles/tab_remove_retention.py:286 +msgid "Calendar weeks with Monday as first day. Current week is ignored." +msgstr "星期一作为第一天的日历周,当前周忽略不计。" -#: ../../qt/settingsdialog.py:680 -msgid "Run 'nice':" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:289 +msgid "12 months periods. Current month is ignored." +msgstr "12 个月的周期,当前月份忽略不计。" -#: ../../qt/settingsdialog.py:686 ../../qt/settingsdialog.py:698 -msgid "as cron job" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:305 +msgid "Retention policy" +msgstr "保留策略" -#: ../../qt/settingsdialog.py:689 ../../qt/settingsdialog.py:704 -#: ../../qt/settingsdialog.py:718 -msgid "on remote host" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:310 +msgid "Run in background on remote host." +msgstr "在远程主机后台运行。" -#: ../../qt/settingsdialog.py:692 -msgid "Run 'ionice':" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:313 +msgid "" +"The retention policy will be executed directly on the remote machine, not " +"locally. The commands \"bash\", \"screen\", and \"flock\" must be installed " +"and available on that remote machine." +msgstr "保留策略将直接在远程机器上执行,而非本地执行。远程机器上必须安装并可用“bash”、“screen”和“flock”命令。" -#: ../../qt/settingsdialog.py:701 -msgid "when taking a manual snapshot" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:317 +msgid "If selected, Back In Time will first test the remote machine." +msgstr "如果选择,Back In Time 将首先测试远程机器。" -#: ../../qt/settingsdialog.py:708 -msgid "Run 'rsync' with 'nocache':" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:321 +msgid "The days are counted starting from today." +msgstr "从今天开始计算天数。" -#: ../../qt/settingsdialog.py:714 -msgid "on local machine" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:322 +msgid "Keep all backups for the last" +msgstr "保留所有备份" -#: ../../qt/settingsdialog.py:721 -msgid "Redirect stdout to /dev/null in cronjobs." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:327 +#: qt/manageprofiles/tab_remove_retention.py:339 +msgid "day(s)." +msgstr "天。" -#: ../../qt/settingsdialog.py:727 -msgid "Redirect stderr to /dev/null in cronjobs." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:334 +msgid "Keep the last backup for each day for the last" +msgstr "保留每天的最后备份" -#: ../../qt/settingsdialog.py:736 -msgid "Limit rsync bandwidth usage: " -msgstr "限制 rsync 使用带宽: " +#: qt/manageprofiles/tab_remove_retention.py:344 +msgid "" +"The weeks are counted starting from the current running week. A week starts " +"on Monday." +msgstr "周数从当前周开始计算,一周从星期一开始。" -#: ../../qt/settingsdialog.py:739 -msgid " KB/sec" -msgstr " KB/秒" +#: qt/manageprofiles/tab_remove_retention.py:347 +msgid "Keep the last backup for each week for the last" +msgstr "保留每周的最后备份" -#: ../../qt/settingsdialog.py:774 -msgid "Preserve ACL" -msgstr "保留 ACL" +#: qt/manageprofiles/tab_remove_retention.py:352 +msgid "week(s)." +msgstr "周。" -#: ../../qt/settingsdialog.py:787 -msgid "Preserve extended attributes (xattr)" -msgstr "保留扩展属性 (xattr)" +#: qt/manageprofiles/tab_remove_retention.py:357 +msgid "" +"The months are counted as calendar months starting with the current month." +msgstr "月份从当前月份开始,按日历月份计算。" -#: ../../qt/settingsdialog.py:805 -msgid "Copy unsafe links (works only with absolute links)" -msgstr "复制不安全的链接(仅绝对链接有效)" +#: qt/manageprofiles/tab_remove_retention.py:360 +msgid "Keep the last backup for each month for the last" +msgstr "保留每月的最后备份" -#: ../../qt/settingsdialog.py:836 -msgid "Paste additional options to rsync" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:365 +msgid "month(s)." +msgstr "月。" -#: ../../qt/settingsdialog.py:839 +#: qt/manageprofiles/tab_remove_retention.py:370 msgid "" -"Options must be quoted e.g. --exclude-from=\"/path/to/my exclude file\"." -msgstr "" +"The years are counted as calendar years starting with the current year." +msgstr "年份从当前年份开始,按日历年份计算。" -#: ../../qt/settingsdialog.py:849 -msgid "Add prefix to SSH commands" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:372 +msgid "Keep the last backup for each year for" +msgstr "保留每年的最后备份" -#: ../../qt/settingsdialog.py:852 -#, python-format -msgid "" -"Prefix to run before every command on remote host.\n" -"Variables need to be escaped with \\$FOO.\n" -"This doesn't touch rsync. So to add a prefix\n" -"for rsync use \"%(cbRsyncOptions)s\" with\n" -"%(rsync_options_value)s\n" -"\n" -"%(default)s: %(def_value)s" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:374 +msgid "all years." +msgstr "所有年份。" -#: ../../qt/settingsdialog.py:860 ../../qt/settingsdialog.py:1741 -msgid "default" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:389 +msgid "… the free space is less than" +msgstr "… 可用空间小于" -#: ../../qt/settingsdialog.py:870 -msgid "Check if remote host is online" -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:394 +msgid "… the free inodes are less than" +msgstr "… 可用索引节点少于" -#: ../../qt/settingsdialog.py:871 -msgid "" -"Warning: if disabled and the remote host\n" -"is not available, this could lead to some\n" -"weird errors." -msgstr "" +#: qt/manageprofiles/tab_remove_retention.py:403 +msgid "Remove oldest backup if …" +msgstr "移除最旧备份,当…" -#: ../../qt/settingsdialog.py:874 -msgid "Check if remote host support all necessary commands" -msgstr "" +#: qt/net.launchpad.backintime.policy:21 +msgid "Authentication is required to run Back In Time as root." +msgstr "需要进行身份验证才能以 root 运行 Back In Time。" -#: ../../qt/settingsdialog.py:875 +#: qt/net.launchpad.backintime.policy:33 qt/net.launchpad.backintime.policy:43 msgid "" -"Warning: if disabled and the remote host\n" -"does not support all necessary commands,\n" -"this could lead to some weird errors." -msgstr "" - -#: ../../qt/settingsdialog.py:888 -msgid "Restore Config" -msgstr "" +"Authentication is required to change backup schedules triggered by external " +"storage device connections." +msgstr "需要进行身份验证才能更改由外部存储设备连接触发的备份计划。" -#: ../../qt/settingsdialog.py:889 -msgid "Edit user-callback" -msgstr "" +#: qt/placeswidget.py:51 +msgid "Shortcuts" +msgstr "快捷方式" -#: ../../qt/settingsdialog.py:908 -msgid "" -"Full system backup can only create a snapshot to be restored to the same " -"physical disk(s) with the same disk partitioning as from the source; " -"restoring to new physical disks or the same disks with different " -"partitioning will yield a potentially broken and unusable system.\n" -"\n" -"Full system backup will override some settings that may have been " -"customized. Continue?" -msgstr "" +#: qt/placeswidget.py:96 +msgid "Places" +msgstr "位置" -#: ../../qt/settingsdialog.py:937 -msgid "New profile" -msgstr "新建配置" +#: qt/placeswidget.py:97 +msgid "File System" +msgstr "文件系统" -#: ../../qt/settingsdialog.py:953 -msgid "Rename profile" -msgstr "重命名配置" +#: qt/placeswidget.py:146 +msgid "Backup directories" +msgstr "备份目录" -#: ../../qt/settingsdialog.py:967 -#, python-format -msgid "Are you sure you want to delete the profile \"%s\" ?" -msgstr "是否确定删除配置“%s”?" +#: qt/qtsystrayicon.py:109 +#, python-brace-format +msgid "Profile: {profile_name}" +msgstr "配置:{profile_name}" -#: ../../qt/settingsdialog.py:1201 -msgid "" -"Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) " -"or */3 for periodic backups every 3 hours" -msgstr "" +#: qt/qtsystrayicon.py:112 +#, python-brace-format +msgid "Profile: {profile_name} (by user \"{desktop_user}\")" +msgstr "配置:{profile_name}(用户“{desktop_user}”)" -#: ../../qt/settingsdialog.py:1237 -msgid "" -"You did not choose a private key file for SSH.\n" -"Would you like to generate a new password-less public/private key pair?" -msgstr "" +#: qt/qtsystrayicon.py:155 +msgid "View Last Log" +msgstr "查看最新日志" -#: ../../qt/settingsdialog.py:1242 -#, python-format -msgid "Private key file \"%(file)s\" does not exist." -msgstr "" +#: qt/qtsystrayicon.py:161 +#, python-brace-format +msgid "Start {appname}" +msgstr "启动 {appname}" -#: ../../qt/settingsdialog.py:1360 -msgid "" -"Would you like to copy your public SSH key to the\n" -"remote host to enable password-less login?" -msgstr "" +#: qt/qtsystrayicon.py:253 +msgid "Working…" +msgstr "运行中…" -#: ../../qt/settingsdialog.py:1376 -#, python-format -msgid "" -"The authenticity of host \"%(host)s\" can't be established.\n" -"\n" -"%(keytype)s key fingerprint is:" -msgstr "" +#: qt/qttools.py:503 +#, python-brace-format +msgid "man page: {man_page_name}" +msgstr "手册页:{man_page_name}" -#: ../../qt/settingsdialog.py:1384 -msgid "" -"Please verify this fingerprint! Would you like to add it to your " -"'known_hosts' file?" -msgstr "" +#: qt/restoreconfigdialog.py:74 +msgid "Import configuration" +msgstr "导入配置" -#: ../../qt/settingsdialog.py:1531 -msgid "Exclude pattern" -msgstr "排除模式" +#: qt/restoreconfigdialog.py:105 +msgid "No directory selected" +msgstr "未选择目录" -#: ../../qt/settingsdialog.py:1544 -msgid "Exclude file" -msgstr "排除文件" +#: qt/restoreconfigdialog.py:134 +msgid "Import" +msgstr "导入" -#: ../../qt/settingsdialog.py:1548 -msgid "Exclude folder" -msgstr "排除文件夹" +#: qt/restoreconfigdialog.py:154 qt/restoreconfigdialog.py:234 +msgid "Searching…" +msgstr "正在搜索…" -#: ../../qt/settingsdialog.py:1567 -msgid "Include file" -msgstr "包含文件" +#: qt/restoreconfigdialog.py:211 +#, python-brace-format +msgid "" +"Select the backup directory from which the configuration file should be " +"imported. The path may look like: {samplePath}" +msgstr "选择应从中导入配置文件的备份目录。示例路径:{samplePath}" -#: ../../qt/settingsdialog.py:1575 ../../qt/settingsdialog.py:1595 -#, python-format +#: qt/restoreconfigdialog.py:216 msgid "" -"\"%s\" is a symlink. The linked target will not be backed up until you " -"include it, too.\n" -"Would you like to include the symlinks target instead?" -msgstr "" +"If the directory is located on an external or remote drive, it must be " +"manually mounted beforehand." +msgstr "如果该目录位于外部或远程驱动器上,则必须事先手动挂载。" -#: ../../qt/settingsdialog.py:1587 -msgid "Include folder" -msgstr "包含文件夹" +#: qt/restoreconfigdialog.py:237 +msgid "Scan again" +msgstr "再次扫描" -#: ../../qt/settingsdialog.py:1614 -msgid "Are you sure you want to change snapshots folder ?" -msgstr "确定要改变快照文件夹吗?" +#: qt/restoreconfigdialog.py:256 +msgid "Show hidden directories" +msgstr "显示隐藏目录" -#: ../../qt/settingsdialog.py:1635 -#, python-format -msgid "Failed to create new SSH key in %(path)s" -msgstr "" +#: qt/restoreconfigdialog.py:258 +msgid "Show/hide hidden directories (Ctrl+H)" +msgstr "显示/隐藏隐藏目录(Ctrl+H)" -#: ../../qt/settingsdialog.py:1738 -msgid "enabled" -msgstr "" +#: qt/restoreconfigdialog.py:305 +msgid "No config found in this directory" +msgstr "此目录中未找到配置" -#: ../../qt/settingsdialog.py:1740 -msgid "disabled" -msgstr "" +#: qt/restoreconfigdialog.py:366 +msgid "Search complete." +msgstr "搜索完成。" -#: ../../qt/settingsdialog.py:1778 -msgid "Restore Settings" -msgstr "" +#: qt/restoredialog.py:58 +msgid "Show full Log" +msgstr "显示完整日志" -#: ../../qt/settingsdialog.py:1795 -msgid " and add your user to group 'fuse'" -msgstr "" +#: qt/shutdowndlg.py:28 +msgid "Countdown to Shutdown" +msgstr "关机倒计时" -#: ../../qt/settingsdialog.py:1799 -#, python-format -msgid "" -"Please navigate to the snapshot from which you want to restore %(appName)s's " -"configuration. The path may look like: \n" -"%(samplePath)s\n" -"\n" -"If your snapshots are on a remote drive or if they are encrypted you need to " -"manually mount them first. If you use Mode SSH you also may need to set up " -"public key login to the remote host%(addFuse)s.\n" -"Take a look at 'man backintime'." -msgstr "" +#: qt/shutdowndlg.py:29 +msgid "The backup has finished." +msgstr "备份已完成。" -#: ../../qt/settingsdialog.py:1857 ../../qt/settingsdialog.py:1920 -msgid "No config found" -msgstr "" +#: qt/shutdowndlg.py:36 +msgid "Cancel Shutdown" +msgstr "取消关机" -#: ../../qt/settingsdialog.py:2096 -msgid "user-callback script has no shebang (#!/bin/sh) line." -msgstr "" +#: qt/shutdowndlg.py:37 +msgid "Shutdown Now" +msgstr "立即关机" -#: ../../qt/settingsdialog.py:2100 -msgid "Shebang in user-callback script is not executable." -msgstr "" +#: qt/shutdowndlg.py:69 +#, python-brace-format +msgid "The system will shut down in {n} second." +msgid_plural "The system will shut down in {n} seconds." +msgstr[0] "系统将在 {n} 秒后关闭。" -#: ../../qt/snapshotsdialog.py:53 ../../qt/snapshotsdialog.py:178 -msgid "Diff Options" -msgstr "Diff 选项" +#: qt/snapshotsdialog.py:61 +msgid "Options about comparing backups" +msgstr "关于比较备份的选项" -#: ../../qt/snapshotsdialog.py:60 +#: qt/snapshotsdialog.py:68 msgid "Command:" msgstr "命令:" -#: ../../qt/snapshotsdialog.py:64 +#: qt/snapshotsdialog.py:72 msgid "Parameters:" msgstr "参数:" -#: ../../qt/snapshotsdialog.py:68 +#: qt/snapshotsdialog.py:77 msgid "Use %1 and %2 for path parameters" msgstr "用 %1 和 %2 作为路径参数" -#: ../../qt/snapshotsdialog.py:111 -msgid "List only different snapshots" -msgstr "仅列出不同的快照" +#: qt/snapshotsdialog.py:94 +msgid "Please set a diff command or press Cancel." +msgstr "请设置 diff 命令或按取消。" -#: ../../qt/snapshotsdialog.py:118 -msgid "List only equal snapshots to: " -msgstr "" +#: qt/snapshotsdialog.py:100 +#, python-brace-format +msgid "" +"The command \"{cmd}\" cannot be found on this system. Please try something " +"else or press Cancel." +msgstr "此系统上找不到命令“{cmd}”。请尝试其他命令或按“取消”。" + +#: qt/snapshotsdialog.py:108 +#, python-brace-format +msgid "No parameters set for the diff command. Using default value \"{params}\"." +msgstr "diff 命令未设置参数。使用默认值“{params}”。" + +#: qt/snapshotsdialog.py:139 qt/timeline.py:117 +msgid "Backups" +msgstr "备份" -#: ../../qt/snapshotsdialog.py:128 +#: qt/snapshotsdialog.py:150 +msgid "Differing backups only" +msgstr "仅差异备份" + +#: qt/snapshotsdialog.py:159 +msgid "List only backups that are equal to:" +msgstr "仅列出与之匹配的备份:" + +#: qt/snapshotsdialog.py:171 msgid "Deep check (more accurate, but slow)" -msgstr "深度检查(更精确,但是较慢)" +msgstr "深度检查(更准确,但较慢)" -#: ../../qt/snapshotsdialog.py:149 +#: qt/snapshotsdialog.py:192 msgid "Delete" msgstr "删除" -#: ../../qt/snapshotsdialog.py:153 +#: qt/snapshotsdialog.py:197 msgid "Select All" -msgstr "选择所有" +msgstr "全选" -#: ../../qt/snapshotsdialog.py:166 -msgid "Diff" -msgstr "Diff" +#: qt/snapshotsdialog.py:217 +msgid "Compare" +msgstr "比较" -#: ../../qt/snapshotsdialog.py:177 +#: qt/snapshotsdialog.py:232 msgid "Go To" msgstr "转到" -#: ../../qt/snapshotsdialog.py:326 -msgid "You can't compare a snapshot to itself" -msgstr "不能将快照与其自身比较" - -#: ../../qt/snapshotsdialog.py:333 -#, python-format -msgid "Command not found: %s" -msgstr "命令未找到:%s" +#: qt/snapshotsdialog.py:234 +msgid "Options" +msgstr "选项" -#: ../../qt/snapshotsdialog.py:361 -#, python-format +#: qt/snapshotsdialog.py:424 +msgid "" +"It is not possible to compare a backup to itself, as the comparison would be" +" redundant." +msgstr "无法将备份与其自身进行比较,因为比较是多余的。" + +#: qt/snapshotsdialog.py:470 +#, python-brace-format +msgid "Really delete {file_or_dir} in backup {backup_id}?" +msgstr "是否确定要删除备份 {backup_id} 中 {file_or_dir}?" + +#: qt/snapshotsdialog.py:475 +#, python-brace-format +msgid "Really delete {file_or_dir} in {count} backups?" +msgstr "是否确定要删除 {count} 个备份中的 {file_or_dir}?" + +#: qt/snapshotsdialog.py:478 +msgid "WARNING: This cannot be revoked." +msgstr "警告:此操作无法撤销。" + +#: qt/snapshotsdialog.py:495 +#, python-brace-format +msgid "Exclude {path} from future backups?" +msgstr "是否从未来的备份中排除 {path}?" + +#: qt/statusbar.py:85 +msgid "Root mode" +msgstr "root 模式" + +#: qt/statusbar.py:87 msgid "" -"Do you really want to delete \"%(file)s\" in snapshot \"%(snapshot_id)s?\n" +"Back In Time is currently running with root privileges (full system access)" +msgstr "Back In Time 当前正在以 root 权限(完整系统访问权限)运行" + +#: qt/timeline.py:64 +msgid "Today" +msgstr "今天" + +#: qt/timeline.py:69 +msgid "Yesterday" +msgstr "昨天" + +#: qt/timeline.py:76 +msgid "This week" +msgstr "本周" + +#: qt/timeline.py:81 +msgid "Last week" +msgstr "上周" + +#: qt/timeline.py:88 +msgid "This month" msgstr "" -#: ../../qt/snapshotsdialog.py:364 -#, python-format -msgid "Do you really want to delete \"%(file)s\" in %(count)d snapshots?\n" +#: qt/timeline.py:95 +msgid "Last month" msgstr "" -#: ../../qt/snapshotsdialog.py:366 -msgid "WARNING: This can not be revoked!" -msgstr "警告:该操作无法撤销!" +#: qt/timeline.py:365 +#, python-brace-format +msgid "Last check {time}" +msgstr "上次检查 {time}" -#: ../../qt/snapshotsdialog.py:381 -#, python-format -msgid "Exclude \"%s\" from future snapshots?" +#: qt/timeline.py:394 +msgid "These are your files as they are right now. It is not a backup." msgstr "" + +#~ msgid "This is NOT a backup but a live view of the local files." +#~ msgstr "这不是备份,而是本地文件的实时视图。" diff --git a/common/po/zh_TW.po b/common/po/zh_TW.po index b17332c32..e8b01f1ca 100644 --- a/common/po/zh_TW.po +++ b/common/po/zh_TW.po @@ -1,1656 +1,2455 @@ -# Chinese (Traditional) translation for backintime -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the backintime package. -# FIRST AUTHOR , 2010. +# SPDX-FileCopyrightText: © 2008 Back In Time Team +# SPDX-FileCopyrightText: © Kuntao Zhao # +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Strict and accurate recording of translators' names began around 2022. As +# the project started in 2008, some earlier translators' names may not have +# been documented and could be lost. msgid "" msgstr "" -"Project-Id-Version: backintime\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-08-20 20:43+0200\n" -"PO-Revision-Date: 2016-02-12 04:12+0000\n" -"Last-Translator: Germar \n" -"Language-Team: Chinese (Traditional) \n" +"Project-Id-Version: Back In Time 2.0.0-dev.5d7ff833\n" +"Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" +"POT-Creation-Date: 2026-03-08 12:51+0100\n" +"PO-Revision-Date: 2026-01-31 12:36+0000\n" +"Last-Translator: ktzhao \n" +"Language-Team: Chinese (Traditional Han script) \n" +"Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Launchpad-Export-Date: 2019-04-14 16:16+0000\n" -"X-Generator: Launchpad (build 18920)\n" +"X-Generator: Weblate 5.15.2\n" -#: ../../common/configfile.py:105 -#, python-format -msgid "Failed to save config: %s" +#: common/bitlicense.py:43 +#, python-brace-format +msgid "" +"All licenses used in this project are located in the {dir_link} directory. " +"To extract per-file license and copyright information using SPDX metadata, " +"refer to {readme_link}." msgstr "" +"本項目所使用的所有授權都位於 {dir_link} 目錄中。若要使用 SPDX 元資料提取每個檔案的許可證和版權信息,請參閱 " +"{readme_link}。" -#: ../../common/configfile.py:136 -#, python-format -msgid "Failed to load config: %s" -msgstr "" +#: common/config.py:37 common/tools.py:87 qt/encfsmsgbox.py:25 +#: qt/messagebox.py:99 qt/timeline.py:33 +msgid "Warning" +msgstr "警告" -#: ../../common/configfile.py:624 ../../common/configfile.py:707 -#, python-format -msgid "Profile \"%s\" already exists !" -msgstr "\"%s\"設定檔已經存在!" +#: common/config.py:109 common/config.py:219 +msgid "Main profile" +msgstr "主要設定檔" -#: ../../common/configfile.py:664 -msgid "You can't remove the last profile !" -msgstr "你不能刪除最後一個設定檔!" +#: common/config.py:225 +msgid "Local (EncFS encrypted)" +msgstr "本地(EncFS 加密)" -#: ../../common/config.py:77 -msgid "Disabled" -msgstr "關閉" +#: common/config.py:226 +msgid "SSH (EncFS encrypted)" +msgstr "SSH(EncFS 加密)" -#: ../../common/config.py:78 -msgid "At every boot/reboot" -msgstr "" +#: common/config.py:237 +msgid "Local" +msgstr "本地端" -#: ../../common/config.py:79 -msgid "Every 5 minutes" -msgstr "每五分鐘" +#: common/config.py:240 +msgid "Local encrypted" +msgstr "本地加密的" -#: ../../common/config.py:80 -msgid "Every 10 minutes" -msgstr "每十分鐘" +#: common/config.py:241 common/config.py:249 common/config.py:256 +#: qt/manageprofiles/tab_general.py:405 +msgid "Encryption" +msgstr "加密" -#: ../../common/config.py:81 -msgid "Every 30 minutes" -msgstr "" +#: common/config.py:245 +msgid "SSH" +msgstr "SSH" -#: ../../common/config.py:82 -msgid "Every hour" -msgstr "" +#: common/config.py:245 common/config.py:255 +#: qt/manageprofiles/tab_general.py:744 +msgid "SSH private key" +msgstr "SSH 私鑰" -#: ../../common/config.py:83 -msgid "Every 2 hours" -msgstr "" +#: common/config.py:300 common/config.py:312 common/config.py:330 +#: common/config.py:344 common/config.py:365 +#, python-brace-format +msgid "Profile: \"{name}\"" +msgstr "設定檔:\"{name}\"" -#: ../../common/config.py:84 -msgid "Every 4 hours" -msgstr "" +#: common/config.py:301 +msgid "Backup directory is not valid." +msgstr "備份目錄不合規定。" -#: ../../common/config.py:85 -msgid "Every 6 hours" -msgstr "" +#: common/config.py:313 +msgid "At least one directory must be selected for backup." +msgstr "至少必須選擇一個目錄進行備份。" -#: ../../common/config.py:86 -msgid "Every 12 hours" -msgstr "" +#: common/config.py:331 common/config.py:346 +#, python-brace-format +msgid "Directory: {path}" +msgstr "目錄: {path}" -#: ../../common/config.py:87 -msgid "Custom Hours" -msgstr "" +#: common/config.py:332 common/config.py:347 +msgid "" +"This directory cannot be included in the backup as it is part of the backup " +"destination itself." +msgstr "該目錄不能包含在備份中,因為它是備份目標本身的一部分。" -#: ../../common/config.py:88 -msgid "Every Day" -msgstr "每天" +#: common/config.py:366 +#, python-brace-format +msgid "" +"The value for \"Remove oldest backup if the free space is less than\" " +"({val_one}) must be less than or equal the threshold for \"Warn if free disk" +" space falls below\" ({val_two})." +msgstr "「如果可用空間小於」的值「刪除最舊的備份」({val_one})必須小於或等於「如果可用磁碟空間低於」的閾值({val_two})。" -#: ../../common/config.py:89 -msgid "Repeatedly (anacron)" -msgstr "" +#: common/config.py:371 +msgid "" +"Please adjust the settings so that the backup removal limit is not higher " +"than the warning limit." +msgstr "請調整設置,使備份刪除限制不高於警告限制。" -#: ../../common/config.py:90 -msgid "When drive get connected (udev)" -msgstr "" +#: common/config.py:1515 +#, python-brace-format +msgid "Udev schedule doesn't work with mode {mode}" +msgstr "udev 定期排程不適用於 {mode} 模式" -#: ../../common/config.py:91 -msgid "Every Week" -msgstr "每週" +#: common/config.py:1569 +msgid "Failed to write new crontab." +msgstr "無法寫入新的 crontab。" -#: ../../common/config.py:92 -msgid "Every Month" -msgstr "每月" +#: common/config.py:1577 +msgid "" +"Cron is not running, even though the crontab command is available. Scheduled" +" backup jobs will not run. Cron might be installed but not enabled. Try " +"running the two commands \"systemctl enable cron\" and \"systemctl start " +"cron\", or consult the support channels of the currently used GNU/Linux " +"distribution for assistance." +msgstr "" +"儘管 crontab 命令可用,但 Cron 並未運行。已排程的備份作業將不會運作。 Cron 可能已安裝但未啟用。嘗試命令「systemctl " +"enable cron」或諮詢您的 GNU Linux 發行版的支援服務商。" + +#: common/configfile.py:101 +msgid "Failed to save config" +msgstr "儲存設定失敗" + +#: common/configfile.py:137 +msgid "Failed to load config" +msgstr "載入設定失敗" + +#: common/configfile.py:679 common/configfile.py:779 +#, python-brace-format +msgid "Profile \"{name}\" already exists." +msgstr "\"{name}\" 設定檔已存在。" + +#: common/configfile.py:725 +msgid "The last profile cannot be removed." +msgstr "無法刪除最後一個設定檔。" + +#: common/encfstools.py:129 common/gocryptfstools.py:84 +#, python-brace-format +msgid "Unable to mount \"{command}\"" +msgstr "無法掛載 “{command}”" + +#: common/encfstools.py:190 +msgid "Configuration for the encrypted directory not found." +msgstr "找不到加密目錄的設定。" + +#: common/encfstools.py:197 +msgid "Create a new encrypted directory?" +msgstr "建立一個新的加密目錄?" + +#: common/encfstools.py:204 +msgid "Cancel" +msgstr "取消" -#: ../../common/config.py:96 ../../common/config.py:103 -msgid "Day(s)" -msgstr "天" +#: common/encfstools.py:209 +msgid "Please re-enter the EncFS password to confirm." +msgstr "請重新輸入 EncFS 密碼進行確認。" -#: ../../common/config.py:97 ../../common/config.py:104 -msgid "Week(s)" -msgstr "週" +#: common/encfstools.py:215 +msgid "The EncFS passwords do not match." +msgstr "EncFS 密碼不符。" -#: ../../common/config.py:98 -msgid "Year(s)" -msgstr "年" +#: common/encfstools.py:659 common/snapshots.py:1128 +msgid "Take snapshot" +msgstr "進行快照" -#: ../../common/config.py:102 -msgid "Hour(s)" -msgstr "" +#: common/gocryptfstools.py:133 +#, python-brace-format +msgid "Unable to init encrypted path \"{command}\"" +msgstr "無法初始化加密路徑“{command}”" + +#: common/mount.py:663 +#, python-brace-format +msgid "Unable to unmount {mountprocess} from {mountpoint}." +msgstr "無法從 {mountpoint} 卸載 {mountprocess}。" + +#: common/mount.py:750 +#, python-brace-format +msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" +msgstr "找不到指令{command}。請安裝它(例如透過“{installcommand}”)" + +#: common/mount.py:774 +#, python-brace-format +msgid "Mountpoint {mntpoint} not empty." +msgstr "掛載點 {mntpoint} 不為空。" + +#: common/password.py:306 +#, python-brace-format +msgid "Enter password for {mode} profile \"{profile}\":" +msgstr "輸入 {mode} 設定檔「{profile}」的密碼:" + +#: common/schedule.py:238 +#, python-brace-format +msgid "" +"Could not install Udev rule for profile {profile_id}. DBus Service " +"'{dbus_interface}' wasn't available." +msgstr "無法為設定檔 {profile_id} 安裝 Udev 規則。 DBus 服務「{dbus_interface}」不可用" -#: ../../common/config.py:105 -msgid "Month(s)" -msgstr "" +#: common/schedule.py:251 +#, python-brace-format +msgid "Couldn't find UUID for {path}" +msgstr "找不到 {path} 的 UUID" -#: ../../common/config.py:126 ../../qt/settingsdialog.py:564 -msgid " EXPERIMENTAL!" -msgstr "" +#: common/snapshots.py:378 common/snapshots.py:656 +msgid "FAILED" +msgstr "失敗" -#: ../../common/config.py:129 -msgid "Local" -msgstr "" +#: common/snapshots.py:579 common/snapshots.py:652 +msgid "Restore permissions" +msgstr "回復權限" -#: ../../common/config.py:130 -msgid "SSH" -msgstr "" +#: common/snapshots.py:656 qt/app.py:207 qt/app.py:1170 qt/app.py:1186 +#: qt/qtsystrayicon.py:118 +msgid "Done" +msgstr "完成" -#: ../../common/config.py:130 ../../common/config.py:132 -#: ../../qt/settingsdialog.py:1626 -msgid "SSH private key" -msgstr "" +#: common/snapshots.py:749 +msgid "" +"The following entries from the include list have no corresponding file or " +"directory in the backup source:" +msgstr "包含清單中的下列項目在備份來源中沒有對應的檔案或目錄:" -#: ../../common/config.py:131 -msgid "Local encrypted" -msgstr "" +#: common/snapshots.py:812 +msgid "Deferring backup while on battery" +msgstr "使用電池時延遲備份" + +#: common/snapshots.py:931 qt/app.py:377 +msgid "Can't find backup directory." +msgstr "找不到快照目錄。" + +#: common/snapshots.py:935 qt/app.py:378 +msgid "If it is on a removable drive, please plug it in." +msgstr "如果它在可移動磁碟中,請插入。" + +#: common/snapshots.py:938 +#, python-brace-format +msgid "Waiting {n} second." +msgid_plural "Waiting {n} seconds." +msgstr[0] "等待{n} 秒。" + +#: common/snapshots.py:1005 +#, python-brace-format +msgid "Failed to create backup {snapshot_id}." +msgstr "建立快照 {snapshot_id} 失敗。" + +#: common/snapshots.py:1037 +msgid "Please be patient. Finalizing…" +msgstr "請耐心等待。正在完成中…" + +#: common/snapshots.py:1163 +msgid "Can't create directory." +msgstr "無法创建目錄。" + +#: common/snapshots.py:1180 +msgid "Saving config file…" +msgstr "正在儲存設定檔…" + +#: common/snapshots.py:1261 +msgid "Saving permissions…" +msgstr "正在儲存權限設定…" + +#: common/snapshots.py:1377 +#, python-brace-format +msgid "Found incomplete backup {snapshot_id} that can be continued." +msgstr "發現尚未完成的快照 {snapshot_id} 可繼續處理。" + +#: common/snapshots.py:1401 +#, python-brace-format +msgid "Removing incomplete {snapshot_id} directory from last run" +msgstr "正在從上次執行中移除不完整的 {snapshot_id} 目錄" + +#: common/snapshots.py:1412 +msgid "Can't remove directory" +msgstr "無法刪除目錄" + +#: common/snapshots.py:1466 +msgid "Creating backup" +msgstr "建立備份" + +#: common/snapshots.py:1517 +msgid "Success" +msgstr "成功" + +#: common/snapshots.py:1520 +msgid "Partial transfer due to error" +msgstr "由於錯誤只轉移部分" + +#: common/snapshots.py:1521 +msgid "Partial transfer due to vanished source files (see 'man rsync')" +msgstr "由於來源檔消失而導致只傳輸部分(請見“man rsync”)" + +#: common/snapshots.py:1525 +#, python-brace-format +msgid "'rsync' ended with exit code {exit_code}" +msgstr "“rsync”已終止退出代碼 {exit_code}" + +#: common/snapshots.py:1538 +msgid "See 'man rsync' for more details" +msgstr "有關更多詳細信息,請參閱“man rsync”" + +#: common/snapshots.py:1545 +msgid "" +"Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" +msgstr "rsync 異常終止,請參閱 “kill -l” 和 “man Kill”" -#: ../../common/config.py:131 ../../common/config.py:132 -msgid "Encryption" -msgstr "" +#: common/snapshots.py:1566 +msgid "Nothing changed, no new backup necessary" +msgstr "沒有任何變化,無需新的備份" -#: ../../common/config.py:132 -msgid "SSH encrypted" -msgstr "" +#: common/snapshots.py:1611 +#, python-brace-format +msgid "Unable to rename {new_path} to {path}." +msgstr "無法將 {new_path} 重新命名為 {path}。" -#: ../../common/config.py:135 -msgid "Default" -msgstr "" +#: common/snapshots.py:1998 +msgid "Applying rules to remove old backups" +msgstr "應用程式規則刪除舊備份" -#: ../../common/config.py:136 -msgid "AES128-CTR" -msgstr "" +#: common/snapshots.py:2031 +msgid "Applying retention policy" +msgstr "正在應用保留策略" -#: ../../common/config.py:137 -msgid "AES192-CTR" -msgstr "" +#: common/snapshots.py:2041 +msgid "Trying to keep min free space" +msgstr "嘗試保留最小剩餘空間" -#: ../../common/config.py:138 -msgid "AES256-CTR" -msgstr "" +#: common/snapshots.py:2079 +#, python-brace-format +msgid "Trying to keep min {perc} free inodes" +msgstr "嘗試保留最小 {perc} 可用 inode" -#: ../../common/config.py:139 -msgid "ARCFOUR256" -msgstr "" +#: common/snapshots.py:3240 qt/app.py:1490 qt/timeline.py:397 +msgid "Now" +msgstr "現在" -#: ../../common/config.py:140 -msgid "ARCFOUR128" -msgstr "" +#: common/sshtools.py:233 +#, python-brace-format +msgid "Unable to mount {sshfs}" +msgstr "無法掛載 {sshfs}" -#: ../../common/config.py:141 -msgid "AES128-CBC" -msgstr "" +#: common/sshtools.py:306 +msgid "ssh-agent not found. Please ensure it is installed." +msgstr "未找到ssh-agent。 請確保它已安裝。" -#: ../../common/config.py:142 -msgid "3DES-CBC" -msgstr "" +#: common/sshtools.py:489 +msgid "" +"Could not unlock ssh private key. Wrong password or password not available " +"for cron." +msgstr "無法解鎖 ssh 私鑰。 密碼錯誤或密碼不可用於 cron。" + +#: common/sshtools.py:721 +msgid "Remote path exists but is not a directory." +msgstr "遠端路徑存在但不是目錄。" + +#: common/sshtools.py:726 +msgid "Remote path is not writable." +msgstr "遠端路徑不可寫入。" + +#: common/sshtools.py:731 +msgid "Remote path is not executable." +msgstr "遠端路徑不可執行。" + +#: common/sshtools.py:736 +msgid "Couldn't create remote path." +msgstr "無法建立遠端路徑。" + +#: common/sshtools.py:1022 +#, python-brace-format +msgid "Remote host {host} doesn't support {command}" +msgstr "遠端主機 {host} 不支援 {command}" + +#: common/sshtools.py:1026 +#, python-brace-format +msgid "Checking commands on host {host} returned unknown error" +msgstr "檢查主機 {host} 上的指令回傳未知錯誤" + +#: common/sshtools.py:1044 +#, python-brace-format +msgid "Remote host {host} doesn't support hardlinks" +msgstr "遠端主機 {host} 不支援硬連結(Hard links)" + +#: common/sshtools.py:1206 +#, python-brace-format +msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." +msgstr "將ssh公鑰“{pubkey}”複製到遠端主機“{host}”。" + +#: common/sshtools.py:1208 +#, python-brace-format +msgid "Please enter a password for \"{user}\"." +msgstr "請輸入「{user}」的密碼。" + +#: common/tools.py:382 +#, python-brace-format +msgid "" +"The destination filesystem for {path} is formatted with NTFS, which has " +"known incompatibilities with Unix-style filesystems." +msgstr "{path} 的目標檔案系統採用 NTFS 格式,已知其與 Unix 風格的檔案系統不相容。" -#: ../../common/config.py:143 -msgid "Blowfish-CBC" -msgstr "" +#: common/tools.py:414 +#, python-brace-format +msgid "{path} is not a valid directory." +msgstr "{path} 不是有效的目錄。" -#: ../../common/config.py:144 -msgid "Cast128-CBC" -msgstr "" +#: common/tools.py:428 +msgid "Creation of following directory failed:" +msgstr "建立以下目錄失敗:" -#: ../../common/config.py:145 -msgid "AES192-CBC" -msgstr "" +#: common/tools.py:430 common/tools.py:527 +msgid "Write access may be restricted." +msgstr "寫入權限可能受到限制。" -#: ../../common/config.py:146 -msgid "AES256-CBC" -msgstr "" +#: common/tools.py:471 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is formatted with FAT which doesn't " +"support hard-links. Please use a native GNU/Linux filesystem." +msgstr "{path} 的檔案系統採用 FAT 格式,不支援硬連結。 請使用原生 Linux 檔案系統。" -#: ../../common/config.py:147 -msgid "ARCFOUR" +#: common/tools.py:482 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via SMB. Please make " +"sure the remote SMB server supports symlinks or activate \"{copyLinks}\" in " +"\"{expertOptions}\"." msgstr "" +"{path} 的目標檔案系統是透過 SMB 掛載共用。請確保遠端 SMB " +"伺服器支援符號連結或在「{expertOptions}」中啟動「{copyLinks}」。" -#: ../../common/config.py:153 -msgid "Main profile" -msgstr "主要設定檔" +#: common/tools.py:486 +msgid "Copy links (dereference symbolic links)" +msgstr "複製鏈結 (dereference symbolic links)" -#: ../../common/config.py:295 ../../common/config.py:302 -#: ../../common/config.py:313 ../../common/config.py:318 -#: ../../qt/qtsystrayicon.py:69 -#, python-format -msgid "Profile: \"%s\"" -msgstr "設定檔:\"%s\"" +#: common/tools.py:487 +msgid "Expert Options" +msgstr "進階選項" -#: ../../common/config.py:295 -msgid "Snapshots folder is not valid !" -msgstr "快照資料夾不存在!" +#: common/tools.py:491 +#, python-brace-format +msgid "" +"Destination filesystem for {path} is a share mounted via sshfs. Sshfs " +"doesn't support hard-links. Please use mode \"SSH\" instead." +msgstr "{path} 的目標檔案系統是透過 sshfs 掛載的共用。 sshfs 不支援硬連結。請改用模式“SSH”。" + +#: common/tools.py:525 +msgid "File creation failed in this directory:" +msgstr "在此目錄中建立檔案失敗:" + +#: qt/aboutdlg.py:50 +msgid "About Back In Time" +msgstr "Back In Time" + +#: qt/aboutdlg.py:88 +msgid "Copyright:" +msgstr "版權:" + +#: qt/aboutdlg.py:92 +msgid "Authors:" +msgstr "作者:" + +#: qt/aboutdlg.py:96 +msgid "Translators:" +msgstr "譯者:" + +#: qt/aboutdlg.py:99 +msgid "translator-credits-placeholder" +msgstr "Kuntao Zhao " + +#: qt/aboutdlg.py:106 +msgid "Translator credits not available for current language." +msgstr "‌當前語言沒有列出翻譯人員資訊。" + +#: qt/aboutdlg.py:115 +msgid "this link" +msgstr "此連結" + +#: qt/aboutdlg.py:117 +#, python-brace-format +msgid "Follow {thislink} to get translator credits for all languages." +msgstr "依照{thislink}取得所有語言的翻譯資訊。" + +#: qt/aboutdlg.py:240 qt/app.py:562 +msgid "Project website" +msgstr "專案網站" + +#: qt/aboutdlg.py:245 qt/app.py:544 +msgid "User manual" +msgstr "使用者手冊" + +#: qt/aboutdlg.py:247 qt/app.py:546 +msgid "Open user manual in browser (local if available, otherwise online)" +msgstr "在瀏覽器中開啟使用者手冊(如果是本機文件則打開,否則線上開啟)" + +#: qt/aboutdlg.py:297 +#, python-brace-format +msgid "{BOLD}Version{BOLDEND}: {version}" +msgstr "{BOLD}版本{BOLDEND}:{version}" + +#: qt/app.py:332 +#, python-brace-format +msgid "" +"{app_name} appears to be running for the first time because no configuration" +" is found." +msgstr "{app_name} 似乎是第一次運行,因為找不到任何配置。" -#: ../../common/config.py:302 -msgid "You must select at least one folder to backup !" -msgstr "必須選擇一個資料夾進行備份" +#: qt/app.py:337 +msgid "" +"Import an existing configuration from a backup location or another computer?" +msgstr "從備份位置或其他電腦匯入現有配置?" -#: ../../common/config.py:313 -msgid "You can't include backup folder !" -msgstr "不能包含備份資料夾" +#: qt/app.py:379 +msgid "Then press OK." +msgstr "然後按“確定”。" -#: ../../common/config.py:318 -msgid "You can't include a backup sub-folder !" -msgstr "不能包含備份子目錄" +#: qt/app.py:483 +msgid "Create a backup" +msgstr "建立備份" -#: ../../common/config.py:373 -#, python-format -msgid "%s is not a folder !" -msgstr "%s並不是資料夾!" +#: qt/app.py:485 +msgid "Use modification time & size for file change detection." +msgstr "使用修改時間和大小來檢測檔案變更。" -#: ../../common/config.py:382 -msgid "Host/User/Profile-ID must not be empty!" -msgstr "" +#: qt/app.py:488 +msgid "Create a backup (checksum mode)" +msgstr "建立快照(校驗模式)" -#: ../../common/config.py:390 ../../common/config.py:422 -#, python-format -msgid "" -"Can't write to: %s\n" -"Are you sure you have write access ?" -msgstr "" -"不能寫入:%s\n" -"請確認是否有寫入權限?" +#: qt/app.py:490 +msgid "Use checksums for file change detection." +msgstr "使用校驗和(checksums)來檢測檔案變更。" -#: ../../common/config.py:402 -#, python-format -msgid "" -"Destination filesystem for '%(path)s' is formatted with FAT which doesn't " -"support hard-links. Please use a native Linux filesystem." -msgstr "" +#: qt/app.py:492 qt/qtsystrayicon.py:125 +msgid "Pause backup process" +msgstr "暫停快照處理" -#: ../../common/config.py:407 -#, python-format -msgid "" -"Destination filsystem for '%(path)s' is a SMB mounted share. Please make " -"sure the remote SMB server supports symlinks or activate '%(copyLinks)s' in " -"'%(expertOptions)s'." -msgstr "" +#: qt/app.py:496 qt/qtsystrayicon.py:130 +msgid "Resume backup process" +msgstr "恢復快照處理" -#: ../../common/config.py:410 ../../qt/settingsdialog.py:817 -msgid "Copy links (dereference symbolic links)" -msgstr "" +#: qt/app.py:500 qt/qtsystrayicon.py:135 +msgid "Stop backup process" +msgstr "停止快照處理" -#: ../../common/config.py:411 ../../qt/settingsdialog.py:671 -msgid "Expert Options" -msgstr "進階選項" +#: qt/app.py:504 +msgid "Refresh backup list" +msgstr "重新整理快照列表" -#: ../../common/config.py:413 -#, python-format -msgid "" -"Destination filesystem for '%(path)s is a sshfs mounted share. sshfs doesn't " -"support hard-links. Please use mode 'SSH' instead." -msgstr "" +#: qt/app.py:508 +msgid "Name backup" +msgstr "命名備份" -#: ../../common/config.py:1474 -msgid "" -"Can't find crontab.\n" -"Are you sure cron is installed ?\n" -"If not you should disable all automatic backups." -msgstr "" -"找不到crontab檔案!\n" -"請確認cron是否安裝?\n" -"如果沒有的話,你應該停用所有自動備份。" +#: qt/app.py:512 +msgid "Remove backup" +msgstr "刪除快照" -#: ../../common/config.py:1478 -msgid "Failed to write new crontab." -msgstr "" +#: qt/app.py:516 +msgid "Open backup log" +msgstr "開啟備份日誌" -#: ../../common/config.py:1575 -#, python-format -msgid "" -"Could not install Udev rule for profile %(profile_id)s. DBus Service " -"'%(dbus_interface)s' wasn't available" -msgstr "" +#: qt/app.py:518 +msgid "View log of the selected backup." +msgstr "查看所選備份的日誌。" -#: ../../common/config.py:1587 -#, python-format -msgid "Schedule udev doesn't work with mode %s" -msgstr "" +#: qt/app.py:520 +msgid "Open last backup log" +msgstr "查看最後的記錄" -#: ../../common/config.py:1596 -#, python-format -msgid "Couldn't find UUID for \"%s\"" -msgstr "" +#: qt/app.py:522 +msgid "View log of the latest backup." +msgstr "查看最新備份的日誌。" -#: ../../common/encfstools.py:86 -#, python-format -msgid "" -"Can't mount '%(command)s':\n" -"\n" -"%(error)s" -msgstr "" +#: qt/app.py:524 +msgid "Manage profiles…" +msgstr "管理設定檔…" -#: ../../common/encfstools.py:132 -msgid "Config for encrypted folder not found." -msgstr "" +#: qt/app.py:528 +msgid "Edit user-callback" +msgstr "編輯用戶回調" -#: ../../common/encfstools.py:136 -msgid "" -"\n" -"Create a new encrypted folder?" -msgstr "" +#: qt/app.py:532 +msgid "Shutdown" +msgstr "關閉" -#: ../../common/encfstools.py:137 -msgid "Cancel" -msgstr "" +#: qt/app.py:534 +msgid "Shut down system after backup has finished." +msgstr "快照完成後關閉系統。" -#: ../../common/encfstools.py:140 -msgid "Please confirm password" -msgstr "" +#: qt/app.py:536 +msgid "Setup language…" +msgstr "設定語言…" -#: ../../common/encfstools.py:144 -msgid "Password doesn't match" -msgstr "" +#: qt/app.py:540 +msgid "Exit" +msgstr "離開" -#: ../../common/encfstools.py:161 -msgid "" -"encfs version 1.7.2 and before has a bug with option --reverse. Please " -"update encfs" -msgstr "" +#: qt/app.py:550 +msgid "man page: Back In Time" +msgstr "手冊頁:Back In &Time" -#: ../../common/encfstools.py:491 ../../common/snapshots.py:795 -#: ../../qt/app.py:87 ../../qt/app.py:91 -msgid "Take snapshot" -msgstr "進行快照" +#: qt/app.py:552 +msgid "Displays man page about Back In Time (backintime)" +msgstr "顯示有關 Back In Time(backintime)的手冊頁" -#: ../../common/mount.py:415 -#, python-format -msgid "" -"Hash collision occurred in hash_id %s. Incrementing global value " -"hash_collision and try again." -msgstr "" +#: qt/app.py:555 +msgid "man page: Profiles config file" +msgstr "手冊頁:設定檔" -#: ../../common/mount.py:490 -#, python-format -msgid "Can't unmount %(proc)s from %(mountpoint)s" -msgstr "" +#: qt/app.py:558 +msgid "Displays man page about profiles config file (backintime-config)" +msgstr "顯示有關設定檔 (backintime-config) 的手冊頁" -#: ../../common/mount.py:577 -#, python-format -msgid "%(proc)s not found. Please install e.g. %(install_command)s" -msgstr "" +#: qt/app.py:565 +msgid "Open Back In Time website in browser" +msgstr "在瀏覽器中開啟 Back In Time 網站" + +#: qt/app.py:567 qt/app.py:2243 +msgid "Changelog" +msgstr "變更日誌" + +#: qt/app.py:570 +msgid "Open changelog (locally if available, otherwise from the web)" +msgstr "開啟變更日誌(如果本機有則打開,否則從網路開啟)" + +#: qt/app.py:573 +msgid "FAQ" +msgstr "常見問題" + +#: qt/app.py:575 +msgid "Open Frequently Asked Questions (FAQ) in browser" +msgstr "在瀏覽器中開啟常見問題 (FAQ)" + +#: qt/app.py:577 +msgid "Ask a question" +msgstr "提出問題" + +#: qt/app.py:581 +msgid "Report a bug" +msgstr "報告錯誤" + +#: qt/app.py:584 +msgid "Translation" +msgstr "翻譯" + +#: qt/app.py:586 +msgid "Shows the message about participation in translation again." +msgstr "再次顯示參與翻譯的訊息。" -#: ../../common/mount.py:590 -#, python-format +#: qt/app.py:590 +msgid "Encryption Transition (EncFS)" +msgstr "加密轉換 (EncFS)" + +#: qt/app.py:592 +msgid "Shows the message about EncFS removal again." +msgstr "再次顯示有關 EncFS 刪除的訊息。" + +#: qt/app.py:599 +msgid "About" +msgstr "關於" + +#: qt/app.py:602 qt/restoredialog.py:46 qt/snapshotsdialog.py:182 +#: qt/snapshotsdialog.py:187 +msgid "Restore" +msgstr "還原" + +#: qt/app.py:604 +msgid "Restore the selected files or directories to the original location." +msgstr "將選定的檔案或目錄還原到原始位置。" + +#: qt/app.py:607 qt/app.py:1886 qt/snapshotsdialog.py:184 +msgid "Restore to …" +msgstr "還原至…" + +#: qt/app.py:609 +#, fuzzy +msgid "Restore the selected files or directories to a new location." +msgstr "將選定的檔案或目錄還原到新的目的地。" + +#: qt/app.py:615 +#, fuzzy msgid "" -"%(user)s is not member of group 'fuse'.\n" -" Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" -"Look at 'man backintime' for further instructions." -msgstr "" +"Restore the currently shown directory and all its contents to the original " +"location." +msgstr "將目前顯示的目錄及其所有內容還原至原始目標。" -#: ../../common/mount.py:611 -#, python-format -msgid "mountpoint %s not empty." -msgstr "" +#: qt/app.py:621 +#, fuzzy +msgid "" +"Restore the currently shown directory and all its contents to a new " +"location." +msgstr "將目前顯示的目錄及其所有內容還原至新目的地。" -#: ../../common/mount.py:674 -msgid "Mountprocess lock timeout" -msgstr "" +#: qt/app.py:624 +msgid "Up" +msgstr "回到上一層" + +#: qt/app.py:627 +msgid "Show hidden files" +msgstr "顯示隱藏檔案" + +#: qt/app.py:630 +msgid "Compare backups…" +msgstr "比較快照…" + +#: qt/app.py:660 qt/app.py:1759 +msgid "Release Candidate" +msgstr "候選版本(Release Candidate)" + +#: qt/app.py:663 +msgid "Shows the message about this Release Candidate again." +msgstr "再次顯示有關此候選版本的消息。" + +#: qt/app.py:700 +msgid "Back In &Time" +msgstr "Back In &Time" + +#: qt/app.py:705 +msgid "&Backup" +msgstr "&備份" + +#: qt/app.py:717 +msgid "&Restore" +msgstr "&還原" + +#: qt/app.py:723 +msgid "&Help" +msgstr "&幫助" -#: ../../common/password.py:240 -#, python-format -msgid "Profile '%(profile)s': Enter password for %(mode)s: " +#: qt/app.py:774 +msgid "Systray Icon" msgstr "" -#: ../../common/snapshotlog.py:62 -msgid "" -"### This log has been decoded with automatic search pattern\n" -"### If some paths are not decoded you can manually decode them with:\n" +#: qt/app.py:780 +msgid "Automatic" msgstr "" -#: ../../common/snapshots.py:321 ../../common/snapshots.py:553 -msgid "FAILED" +#: qt/app.py:784 +msgid "Light icon" msgstr "" -#: ../../common/snapshots.py:500 ../../common/snapshots.py:556 -msgid "Restore permissions:" +#: qt/app.py:785 +msgid "Dark icon" msgstr "" -#: ../../common/snapshots.py:555 ../../qt/app.py:432 ../../qt/app.py:675 -#: ../../qt/app.py:708 ../../qt/qtsystrayicon.py:73 -msgid "Done" -msgstr "完成" +#: qt/app.py:808 +msgid "Icons only" +msgstr "僅限圖示" -#: ../../common/snapshots.py:609 -msgid "Deferring backup while on battery" +#: qt/app.py:811 +msgid "Text only" +msgstr "限文字" + +#: qt/app.py:814 +msgid "Text below icons" +msgstr "圖示下方的文字" + +#: qt/app.py:817 +msgid "Text beside icon" +msgstr "圖示旁邊的文字" + +#: qt/app.py:943 +#, fuzzy +msgid "This directory doesn't exist in the selected backup." msgstr "" +"在目前選定的快照中\n" +"不存在該目錄。" -#: ../../common/snapshots.py:669 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it." +#: qt/app.py:946 +#, fuzzy +msgid "This directory doesn't exist on the computer." msgstr "" -"快照資料夾不存在。\n" -"若存在於移動儲存設備,請插入裝置" +"在目前選定的快照中\n" +"不存在該目錄。" -#: ../../common/snapshots.py:671 -#, python-format -msgid "Waiting %s second." -msgid_plural "Waiting %s seconds." -msgstr[0] "等待%s秒。" +#: qt/app.py:995 +msgid "" +"If this window is closed, Back In Time will not be able to shut down your " +"system when the backup is finished." +msgstr "如果您關閉此窗口,Back In Time 將無法在快照完成後關閉您的系統。" -#: ../../common/snapshots.py:703 -#, python-format -msgid "Failed to take snapshot %s !!!" -msgstr "快照 %s 執行失敗!!!" +#: qt/app.py:998 +msgid "Close the window anyway?" +msgstr "仍要關閉視窗?" -#: ../../common/snapshots.py:712 -msgid "Finalizing" -msgstr "關閉光碟中" +#: qt/app.py:1189 +msgid "Done, no backup needed" +msgstr "已完成,不需要進行備份" -#: ../../common/snapshots.py:823 -#, python-format -msgid "Can't create folder: %s" -msgstr "無法新增資料夾:%s" +#: qt/app.py:1260 +msgid "Working:" +msgstr "運作中 :" -#: ../../common/snapshots.py:836 -msgid "Saving config file..." -msgstr "保留組態檔..." +#: qt/app.py:1267 +msgid "Working" +msgstr "運作中" -#: ../../common/snapshots.py:873 -msgid "Saving permissions..." -msgstr "保留存取權限..." +#: qt/app.py:1273 qt/messagebox.py:136 +msgid "Error" +msgstr "錯誤" -#: ../../common/snapshots.py:955 -msgid "..." -msgstr "..." +#: qt/app.py:1314 qt/qtsystrayicon.py:295 +msgid "Sent:" +msgstr "傳送:" -#: ../../common/snapshots.py:963 -#, python-format -msgid "Found leftover '%s' which can be continued." -msgstr "" +#: qt/app.py:1315 qt/qtsystrayicon.py:296 +msgid "Speed:" +msgstr "速度:" -#: ../../common/snapshots.py:976 -#, python-format -msgid "Removing leftover '%s' folder from last run" -msgstr "" +#: qt/app.py:1316 qt/qtsystrayicon.py:297 +msgid "ETA:" +msgstr "ETA:" -#: ../../common/snapshots.py:981 -#, python-format -msgid "Can't remove folder: %s" -msgstr "無法刪除資料夾:%s" +#: qt/app.py:1498 +#, fuzzy +msgid "Backup:" +msgstr "備份:" -#: ../../common/snapshots.py:1015 -msgid "Taking snapshot" -msgstr "進行快照" +#: qt/app.py:1598 +#, python-brace-format +msgid "Restore {path}" +msgstr "還原{path}" -#: ../../common/snapshots.py:1049 -msgid "Nothing changed, no new snapshot necessary" -msgstr "" +#: qt/app.py:1600 +#, python-brace-format +msgid "Restore {path} to …" +msgstr "還原{path}到…" -#: ../../common/snapshots.py:1075 -#, python-format -msgid "Can't rename %(new_path)s to %(path)s" -msgstr "" +#: qt/app.py:1624 +#, python-brace-format +msgid "" +"Hello\n" +"You have used Back In Time in the {language} language a few times by now.\n" +"The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" +"Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" +"We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" +"Your Back In Time Team" +msgstr "" +"您好,\n" +"您已經多次以 {language} 使用 Back In Time 了。\n" +"當前安裝的 Back In Time 所使用的 {language} 翻譯完成了 {perc}。無論您技術能力如何,都可以向 Back In Time 貢獻翻譯。\n" +"若您希望參與翻譯,請訪問 {translation_platform_url}。如需協助或瞭解詳情,請訪問 {back_in_time_project_website}。\n" +"非常抱歉打斷您的使用,本訊息今後不會再顯示。此對話框可以隨時在「幫助」菜單內呼出。\n" +"Back In Time 團隊" + +#: qt/app.py:1653 +msgid "translation platform" +msgstr "翻譯平台" + +#: qt/app.py:1658 +msgid "Website" +msgstr "網站" -#: ../../common/snapshots.py:1366 ../../common/snapshots.py:1417 -#: ../../qt/settingsdialog.py:555 -msgid "Smart remove" -msgstr "智慧移除" +#: qt/app.py:1672 +msgid "Your translation" +msgstr "您的翻譯" -#: ../../common/snapshots.py:1393 -msgid "Removing old snapshots" -msgstr "刪除快照" +#: qt/app.py:1709 +#, python-brace-format +msgid "In the Fediverse at Mastodon: {link_and_label}." +msgstr "在聯邦宇宙 Mastodon:{link_and_label}" -#: ../../common/snapshots.py:1427 -msgid "Trying to keep min free space" -msgstr "嘗試保留最小剩餘空間" +#: qt/app.py:1714 +#, fuzzy, python-brace-format +msgid "Email to {link_and_label}." +msgstr "郵件清單 {link_and_label}" -#: ../../common/snapshots.py:1464 -#, python-format -msgid "Trying to keep min %d%% free inodes" -msgstr "" +#: qt/app.py:1718 +#, python-brace-format +msgid "Mailing list {link_and_label}." +msgstr "郵件清單 {link_and_label}" -#: ../../common/snapshots.py:2040 -msgid "WITH ERRORS !" -msgstr "" +#: qt/app.py:1722 +#, python-brace-format +msgid "{link_and_label} on the project website." +msgstr "項目網站上的 {link_and_label}。" -#: ../../common/snapshots.py:2505 ../../qt/app.py:251 ../../qt/app.py:1306 -msgid "Now" -msgstr "現在" +#: qt/app.py:1725 +msgid "Open an issue" +msgstr "打開問題" -#: ../../common/sshtools.py:163 -#, python-format -msgid "Can't mount %s" -msgstr "" +#: qt/app.py:1726 +msgid "Alternatively, you can use another channel of your choice." +msgstr "或者,您也可以使用您選擇的其他管道。" -#: ../../common/sshtools.py:315 +#: qt/app.py:1731 +#, python-brace-format msgid "" -"Could not unlock ssh private key. Wrong password or password not available " -"for cron." -msgstr "" +"This version of Back In Time is a Release Candidate and is primarily intended for stability testing in preparation for the next official release.\n" +"No user data or telemetry is collected. However, the Back In Time team is very interested in knowing if the Release Candidate is being used and if it is worth continuing to provide such pre-release versions.\n" +"Therefore, the team kindly asks for a short feedback on whether you have tested this version, even if you didn’t encounter any issues. Even a quick test run of a few minutes would help us a lot.\n" +"The following contact options are available:\n" +"{contact_list}\n" +"In this version, this message won't be shown again but can be accessed anytime through the help menu.\n" +"Thank you for your support and for helping us improve Back In Time!\n" +"Your Back In Time Team" +msgstr "" +"此版本的“Back In Time”為候選版本,主要用於穩定性測試,為下一個正式版本做準備。\n" +"不收集任何用戶資料或遙測資料。然而,Back In Time 團隊非常有興趣了解候選版本是否正在使用,以及是否值得繼續提供這樣的預發布版本。\n" +"因此,即使您沒有遇到任何問題,團隊仍懇請您簡短回饋是否已經測試過此版本。即使只是幾分鐘的快速測試運行也會對我們有很大幫助。\n" +"可用的聯絡方式如下:\n" +"{contact_list}\n" +"在此版本中,此訊息不會再次顯示,但可以隨時透過說明選單存取。\n" +"感謝您的支持並幫助我們改進“Back In Time”!\n" +"您的 Back In Time團隊" + +#: qt/app.py:1836 +#, python-brace-format +msgid "" +"Only {free} free space available on the destination, which is below the " +"configured threshold of {threshold}." +msgstr "目標上僅有 {free} 可用空間,低於配置的閾值 {threshold}。" -#: ../../common/sshtools.py:345 -#, python-format +#: qt/app.py:1841 +msgid "Proceed with the backup?" +msgstr "是否繼續備份?" + +#: qt/app.py:1866 +#, python-brace-format +msgid "All newer files in {path} will be removed. Proceed?" +msgstr "{path} 中所有較新的檔案都將被刪除。是否繼續?" + +#: qt/app.py:1869 +msgid "All newer files in the original directory will be removed. Proceed?" +msgstr "原始目錄中所有較新的檔案都將被刪除。是否繼續?" + +#: qt/app.py:1875 +#, python-brace-format msgid "" -"Password-less authentication for %(user)s@%(host)s failed. Look at 'man " -"backintime' for further instructions." +"{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " +"the entire system." +msgstr "{BOLD}警告{BOLDEND}:刪除檔案系統根目錄中的檔案可能會破壞整個系統。" + +#: qt/app.py:2107 +msgid "Backup name" +msgstr "備份" + +#: qt/app.py:2138 +msgid "Remove this backup?" +msgid_plural "Remove these backups?" +msgstr[0] "刪除此備份嗎?" + +#: qt/app.py:2204 +msgid "The language settings take effect only after restarting Back In Time." +msgstr "語言設定僅在重新啟動 Back In Time 後生效。" + +#: qt/backintime-qt-root.desktop:14 +msgid "Versioned Backups (root)" msgstr "" -#: ../../common/sshtools.py:377 -#, python-format +#: qt/backintime-qt-root.desktop:23 msgid "" -"Cipher %(cipher)s failed for %(host)s:\n" -"%(err)s" +"User-friendly GUI for versioned backups that reduces disk usage (root mode)" msgstr "" -#: ../../common/sshtools.py:428 -#, python-format -msgid "%s not found in ssh_known_hosts." -msgstr "" +#: qt/backintime-qt.desktop:14 +#, fuzzy +msgid "Versioned Backups" +msgstr "保留命名的快照。" -#: ../../common/sshtools.py:468 -#, python-format -msgid "" -"Remote path exists but is not a directory:\n" -" %s" +#: qt/backintime-qt.desktop:23 +msgid "User-friendly GUI for versioned backups that reduces disk usage" msgstr "" -#: ../../common/sshtools.py:470 -#, python-format +#: qt/confirmrestoredialog.py:35 qt/messagebox.py:123 +msgid "Question" +msgstr "問題" + +#: qt/confirmrestoredialog.py:76 +#, fuzzy, python-brace-format msgid "" -"Remote path is not writable:\n" -" %s" +"Create backup copies with trailing {suffix} before overwriting or removing " +"local elements." msgstr "" +"建立帶有尾隨 {suffix} 的備份副本\n" +"在覆蓋或刪除本地元素之前。" -#: ../../common/sshtools.py:472 -#, python-format +#: qt/confirmrestoredialog.py:81 qt/manageprofiles/tab_options.py:69 +#, python-brace-format msgid "" -"Remote path is not executable:\n" -" %s" -msgstr "" +"Before restoring, newer versions of files will be renamed with the appended " +"{suffix}. These files can be removed with the following command:" +msgstr "在復原之前,較新版本的檔案將使用尾隨 {suffix} 進行重新命名。如果您不再需要它們,可以使用以下命令刪除它們:" -#: ../../common/sshtools.py:474 -#, python-format +#: qt/confirmrestoredialog.py:94 +#, fuzzy, python-brace-format msgid "" -"Couldn't create remote path:\n" -" %s" +"Only restore elements which do not exist or are newer than those in " +"destination. Using \"{rsync_example}\" option." msgstr "" +"只恢復不存在或\n" +"比目的地新的元素。\n" +"使用“rsync --update”選項。" -#: ../../common/sshtools.py:510 -#, python-format -msgid "Ping %s failed. Host is down or wrong address." -msgstr "" +#: qt/confirmrestoredialog.py:133 +msgid "Remove newer elements in original directory." +msgstr "刪除原始資料夾中較新的元素。" -#: ../../common/sshtools.py:577 ../../common/sshtools.py:683 -#, python-format +#: qt/confirmrestoredialog.py:135 msgid "" -"Remote host %(host)s doesn't support '%(command)s':\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Restore selected files or directories to the original destination and delete" +" files or directories which are not in the backup. Be extremely careful " +"because this will delete files and directories which were excluded during " +"the creation of the backup." +msgstr "將選定的檔案或目錄還原到原始目標,並刪除快照中不存在的檔案或目錄。請務必小心,因為這將刪除在拍攝快照時排除的檔案和目錄。" + +#: qt/confirmrestoredialog.py:147 +msgid "Really restore this element into the new directory?" +msgid_plural "Really restore these elements into the new directory?" +msgstr[0] "真的要將該元素還原到新目錄中嗎?" + +#: qt/confirmrestoredialog.py:157 +msgid "Really restore this element?" +msgid_plural "Really restore these elements?" +msgstr[0] "是否確定要恢復所選元素?" + +#: qt/editusercallback.py:43 +#, python-brace-format +msgid "User-callback: \"{filename}\"" +msgstr "使用者回呼:“{filename}”" + +#: qt/editusercallback.py:105 +#, python-brace-format +msgid "" +"The user-callback script must include a shebang on the first line (e.g. " +"{example})." +msgstr "使用者回呼腳本必須在第一行包含一個 shebang(例如 {example})。" + +#: qt/filedialog.py:87 +msgid "Show/hide hidden files and directories (Ctrl+H)" +msgstr "顯示/隱藏檔案和目錄(Ctrl+H)" + +#: qt/fileview.py:204 +msgid "Add to Include" +msgstr "加入包含項目" + +#: qt/fileview.py:205 +msgid "Add to Exclude" +msgstr "加入排除項目" + +#: qt/fileview.py:292 +msgid "The selected item is already in the include list." +msgid_plural "The selected items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:299 +msgid "The following item is already in the include list." +msgid_plural "The following items are already in the include list." +msgstr[0] "" + +#: qt/fileview.py:313 +msgid "The selected item is already in the exclude list." +msgid_plural "The selected items are already in the exclude list." +msgstr[0] "" + +#: qt/fileview.py:320 +msgid "The following item is already in the exclude list." +msgid_plural "The following items are already in the exclude list." +msgstr[0] "" + +#: qt/languagedialog.py:36 +msgid "Setup language" +msgstr "設定語言" + +#: qt/languagedialog.py:120 +#, python-brace-format +msgid "Translated: {percent}" +msgstr "已翻譯: {percent}" + +#: qt/languagedialog.py:132 +msgid "System default" +msgstr "系統預設" + +#: qt/languagedialog.py:142 +msgid "Use operating system's language." +msgstr "使用作業系統語言。" + +#: qt/logviewdialog.py:63 +msgid "Backup Log View" +msgstr "備份日誌視圖" + +#: qt/logviewdialog.py:63 +msgid "Last Log View" +msgstr "最後一次日誌查看" + +#: qt/logviewdialog.py:71 qt/manageprofiles/__init__.py:72 +#: qt/manageprofiles/tab_general.py:250 qt/restoreconfigdialog.py:343 +msgid "Profile:" +msgstr "設定:" + +#: qt/logviewdialog.py:86 +msgid "Backups:" +msgstr "備份:" + +#: qt/logviewdialog.py:93 +msgid "Filter:" +msgstr "篩選:" + +#: qt/logviewdialog.py:100 +msgid "[E] Error, [I] Information, [C] Change" +msgstr "[E] 錯誤,[I] 訊息,[C] 更改" + +#: qt/logviewdialog.py:103 qt/qtsystrayicon.py:148 +msgid "decode paths" +msgstr "解碼路徑" + +#: qt/logviewdialog.py:122 qt/manageprofiles/copylinkswidget.py:56 +#: qt/manageprofiles/tab_options.py:188 +msgid "All" +msgstr "全部" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:132 +#: qt/manageprofiles/tab_options.py:187 +msgid "Changes" +msgstr "變化" + +#: qt/logviewdialog.py:128 qt/logviewdialog.py:131 +#: qt/manageprofiles/tab_options.py:186 qt/manageprofiles/tab_options.py:187 +msgid "Errors" +msgstr "錯誤" + +#: qt/logviewdialog.py:133 qt/messagebox.py:75 +msgid "Information" +msgid_plural "Information" +msgstr[0] "資訊" + +#: qt/logviewdialog.py:135 +msgid "rsync transfer failures (experimental)" +msgstr "rsync 傳輸失敗(實驗性)" + +#: qt/manageprofiles/__init__.py:64 +msgid "Manage profiles" +msgstr "管理設定檔" + +#: qt/manageprofiles/__init__.py:83 +msgid "Edit" +msgstr "編輯" + +#: qt/manageprofiles/__init__.py:87 +msgid "Add" +msgstr "添加" + +#: qt/manageprofiles/__init__.py:91 qt/manageprofiles/tab_exclude.py:125 +#: qt/manageprofiles/tab_include.py:79 +msgid "Remove" +msgstr "消除" + +#: qt/manageprofiles/__init__.py:112 +msgid "&General" +msgstr "&一般的" + +#: qt/manageprofiles/__init__.py:116 +msgid "&Include" +msgstr "包含" + +#: qt/manageprofiles/__init__.py:120 +msgid "&Exclude" +msgstr "&排除" + +#: qt/manageprofiles/__init__.py:135 +msgid "&Remove & Retention" +msgstr "移除 & 保留" + +#: qt/manageprofiles/__init__.py:139 +msgid "&Options" +msgstr "選項" + +#: qt/manageprofiles/__init__.py:143 +msgid "E&xpert Options" +msgstr "專家選項" + +#: qt/manageprofiles/__init__.py:151 +msgid "Restore Config" +msgstr "恢復配置" + +#: qt/manageprofiles/__init__.py:182 +msgid "New profile" +msgstr "新增設定檔" + +#: qt/manageprofiles/__init__.py:199 +msgid "Rename profile" +msgstr "重新命名設定檔" + +#: qt/manageprofiles/__init__.py:215 +#, python-brace-format +msgid "Delete the profile \"{name}\"?" +msgstr "確認刪除設定檔\"{name}\"?" + +#: qt/manageprofiles/copylinkswidget.py:38 +msgid "Copy symbolic links as files" msgstr "" -#: ../../common/sshtools.py:686 -#, python-format +#: qt/manageprofiles/copylinkswidget.py:43 msgid "" -"Check commands on host %(host)s returned unknown error:\n" -"%(err)s\n" -"Look at 'man backintime' for further instructions" +"Copy symbolic links as real files or directories in the backup. Select " +"whether all links or only those pointing outside the source are copied." msgstr "" -#: ../../common/sshtools.py:699 -#, python-format -msgid "Remote host %s doesn't support hardlinks" +#: qt/manageprofiles/copylinkswidget.py:46 +msgid "This option may increase backup size." msgstr "" -#: ../../common/sshtools.py:761 -#, python-format -msgid "" -"Copy public ssh-key \"%(pubkey)s\" to remote host \"%(host)s\".\n" -"Please enter password for \"%(user)s\":" +#: qt/manageprofiles/copylinkswidget.py:47 +msgid "Disabled by default." msgstr "" -#: ../../qt/app.py:93 -msgid "Take snapshot with checksums" +#: qt/manageprofiles/copylinkswidget.py:60 +msgid "" +"All symbolic links are replaced with real files or directories they point " +"to. This increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/app.py:94 ../../qt/settingsdialog.py:643 -msgid "Use checksum to detect changes" +#: qt/manageprofiles/copylinkswidget.py:63 +msgid "Uses 'rsync --copy-links'." msgstr "" -#: ../../qt/app.py:102 ../../qt/qtsystrayicon.py:78 -msgid "Pause snapshot process" +#: qt/manageprofiles/copylinkswidget.py:68 +msgid "Only external" msgstr "" -#: ../../qt/app.py:108 ../../qt/qtsystrayicon.py:82 -msgid "Resume snapshot process" +#: qt/manageprofiles/copylinkswidget.py:72 +msgid "" +"Only links pointing outside the backup source are copied as files. This " +"increases backup size and may store the same files multiple times." msgstr "" -#: ../../qt/app.py:114 ../../qt/qtsystrayicon.py:87 -msgid "Stop snapshot process" +#: qt/manageprofiles/copylinkswidget.py:75 +msgid "Uses 'rsync --copy-unsafe-links'." msgstr "" -#: ../../qt/app.py:119 -msgid "Refresh snapshots list" +#: qt/manageprofiles/excludesuggestions.py:33 +msgid "Editor & Office temporary files" msgstr "" -#: ../../qt/app.py:123 ../../qt/app.py:898 -msgid "Snapshot Name" -msgstr "快照名稱" +#: qt/manageprofiles/excludesuggestions.py:34 +#, fuzzy +msgid "Emacs backup files" +msgstr "暫停快照處理" -#: ../../qt/app.py:126 -msgid "Remove Snapshot" -msgstr "刪除快照" +#: qt/manageprofiles/excludesuggestions.py:35 +#, fuzzy +msgid "Emacs autosave files" +msgstr "排除檔案" -#: ../../qt/app.py:129 -msgid "View Snapshot Log" +#: qt/manageprofiles/excludesuggestions.py:36 +msgid "Vim swap files" msgstr "" -#: ../../qt/app.py:132 ../../qt/qtsystrayicon.py:96 -msgid "View Last Log" +#: qt/manageprofiles/excludesuggestions.py:37 +msgid "Microsoft Office temporary files" msgstr "" -#: ../../qt/app.py:137 ../../qt/settingsdialog.py:59 -msgid "Settings" -msgstr "設定" - -#: ../../qt/app.py:142 -msgid "Shutdown" +#: qt/manageprofiles/excludesuggestions.py:40 +msgid "LibreOffice & other OpenDocument Editors lock files" msgstr "" -#: ../../qt/app.py:143 -msgid "Shutdown system after snapshot has finished." +#: qt/manageprofiles/excludesuggestions.py:44 +msgid "Thumbnails & Temporary Pictures" msgstr "" -#: ../../qt/app.py:149 -msgid "Exit" -msgstr "離開" - -#: ../../qt/app.py:158 ../../qt/app.py:177 ../../qt/app.py:287 -msgid "Help" -msgstr "求助" - -#: ../../qt/app.py:160 -msgid "Config File Help" +#: qt/manageprofiles/excludesuggestions.py:47 +msgid "Thumbnail cache on GNU/Linux and other unixoid OS'es" msgstr "" -#: ../../qt/app.py:163 -msgid "Website" -msgstr "網站" +#: qt/manageprofiles/excludesuggestions.py:50 +msgid "Thumbnail database on Windows" +msgstr "" -#: ../../qt/app.py:165 ../../qt/app.py:993 -msgid "Changelog" +#: qt/manageprofiles/excludesuggestions.py:51 +msgid "Metadata directory on MacOS" msgstr "" -#: ../../qt/app.py:167 -msgid "FAQ" +#: qt/manageprofiles/excludesuggestions.py:53 +msgid "Application-specific locks" msgstr "" -#: ../../qt/app.py:169 -msgid "Ask a question" +#: qt/manageprofiles/excludesuggestions.py:56 +msgid "Discord application lock file" msgstr "" -#: ../../qt/app.py:171 -msgid "Report a bug" +#: qt/manageprofiles/excludesuggestions.py:57 +msgid "Discord session lock file" msgstr "" -#: ../../qt/app.py:174 ../../qt/app.py:1439 -msgid "About" -msgstr "關於" +#: qt/manageprofiles/excludesuggestions.py:60 +msgid "Mozilla Firefox & Thunderbird lock file" +msgstr "" -#: ../../qt/app.py:204 -msgid "Up" -msgstr "向上" +#: qt/manageprofiles/excludesuggestions.py:62 +#, fuzzy +msgid "Caches & Temporary directories" +msgstr "備份目錄" -#: ../../qt/app.py:214 ../../qt/settingsdialog.py:1839 -msgid "Show hidden files" -msgstr "顯示隱藏檔案" +#: qt/manageprofiles/excludesuggestions.py:63 +msgid "User application cache" +msgstr "" -#: ../../qt/app.py:224 ../../qt/app.py:247 ../../qt/app.py:280 -#: ../../qt/restoredialog.py:51 ../../qt/settingsdialog.py:1881 -#: ../../qt/snapshotsdialog.py:139 ../../qt/snapshotsdialog.py:144 -msgid "Restore" -msgstr "還原" +#: qt/manageprofiles/excludesuggestions.py:64 +#: qt/manageprofiles/excludesuggestions.py:67 +#, fuzzy +msgid "System temporary directory" +msgstr "無法刪除目錄" -#: ../../qt/app.py:225 -msgid "Restore the selected files or folders to the original destination." +#: qt/manageprofiles/excludesuggestions.py:72 +msgid "Package cache for Debian(-based) GNU/Linux distributions" msgstr "" -#: ../../qt/app.py:228 ../../qt/app.py:1131 ../../qt/app.py:1163 -#: ../../qt/snapshotsdialog.py:141 -msgid "Restore to ..." +#: qt/manageprofiles/excludesuggestions.py:77 +msgid "Flatpak app and runtime repository" msgstr "" -#: ../../qt/app.py:229 -msgid "Restore the selected files or folders to a new destination." -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:81 +#, fuzzy +msgid "System runtime directories" +msgstr "顯示隱藏檔案" -#: ../../qt/app.py:234 -msgid "" -"Restore the currently shown folder and all its content to the original " -"destination." +#: qt/manageprofiles/excludesuggestions.py:84 +msgid "Kernel and process information" msgstr "" -#: ../../qt/app.py:239 -msgid "" -"Restore the currently shown folder and all its content to a new destination." +#: qt/manageprofiles/excludesuggestions.py:89 +msgid "Device and other hardware information (sysfs interface)" msgstr "" -#: ../../qt/app.py:249 -#, python-format -msgid "" -"Restore selected file or folder.\n" -"If this button is grayed out this is most likely because \"%(now)s\" is " -"selected in left hand snapshots list." +#: qt/manageprofiles/excludesuggestions.py:92 +msgid "Device nodes" msgstr "" -#: ../../qt/app.py:254 ../../qt/logviewdialog.py:75 ../../qt/qttools.py:200 -#: ../../qt/snapshotsdialog.py:101 -msgid "Snapshots" -msgstr "快照" - -#: ../../qt/app.py:260 -msgid "Snapshot" +#: qt/manageprofiles/excludesuggestions.py:93 +msgid "Runtime system files" msgstr "" -#: ../../qt/app.py:271 -msgid "View" +#: qt/manageprofiles/excludesuggestions.py:95 +msgid "Other non-persistent" msgstr "" -#: ../../qt/app.py:321 -msgid "Shortcuts" -msgstr "快速鍵" - -#: ../../qt/app.py:337 -msgid "" -"This folder doesn't exist\n" -"in the current selected snapshot!" +#: qt/manageprofiles/excludesuggestions.py:98 +msgid "List of currently mounted filesystems" msgstr "" -#: ../../qt/app.py:398 -msgid "Add to Include" +#: qt/manageprofiles/excludesuggestions.py:101 +msgid "System swap file (virtual memory)" msgstr "" -#: ../../qt/app.py:399 ../../qt/logviewdialog.py:175 -msgid "Add to Exclude" +#: qt/manageprofiles/excludesuggestions.py:102 +msgid "GNOME virtual file system mount point" msgstr "" -#: ../../qt/app.py:471 -#, python-format -msgid "" -"%(appName)s is not configured. Would you like to restore a previous " -"configuration?" +#: qt/manageprofiles/excludesuggestions.py:103 +msgid "Recovered filesystem objects" msgstr "" -#: ../../qt/app.py:492 -msgid "" -"Can't find snapshots folder.\n" -"If it is on a removable drive please plug it and then press OK" +#: qt/manageprofiles/excludesuggestions.py:105 +msgid "Miscellaneous" msgstr "" -"快照資料夾不存在。\n" -"若存在於移動儲存設備,請插入裝置並確認" -#: ../../qt/app.py:527 -msgid "" -"If you close this window Back In Time will not be able to shutdown your " -"system when the snapshot has finished.\n" -"Do you really want to close?" +#: qt/manageprofiles/excludesuggestions.py:108 +msgid "Metadata directory on Microsoft Windows" msgstr "" -#: ../../qt/app.py:667 ../../qt/app.py:719 -msgid "Working:" -msgstr "運作中 :" +#: qt/manageprofiles/excludesuggestions.py:111 +msgid "User recycle bin" +msgstr "" -#: ../../qt/app.py:711 -msgid "Done, no backup needed" -msgstr "已完成,不需要進行備份" +#: qt/manageprofiles/excludesuggestions.py:112 +#, fuzzy +msgid "System backup files" +msgstr "停止快照處理" -#: ../../qt/app.py:723 -msgid "Error:" -msgstr "錯誤:" +#: qt/manageprofiles/excludesuggestions.py:139 +#, fuzzy +msgid "Exclude Suggestions" +msgstr "排除目錄" -#: ../../qt/app.py:743 ../../qt/qtsystrayicon.py:188 -msgid "Sent:" +#: qt/manageprofiles/excludesuggestions.py:144 +msgid "Select commonly used items to add to backup exclusions." msgstr "" -#: ../../qt/app.py:744 ../../qt/qtsystrayicon.py:189 -msgid "Speed:" -msgstr "" +#: qt/manageprofiles/excludesuggestions.py:157 +#, fuzzy +msgid "Default" +msgstr "預設" -#: ../../qt/app.py:745 ../../qt/qtsystrayicon.py:190 -msgid "ETA:" +#: qt/manageprofiles/excludesuggestions.py:158 +msgid "Reset to predefined selection" msgstr "" -#: ../../qt/app.py:794 -msgid "Global" -msgstr "全域設定" +#: qt/manageprofiles/schedulewidget.py:38 +msgid "Schedule" +msgstr "排程" -#: ../../qt/app.py:795 -msgid "Root" -msgstr "主目錄" +#: qt/manageprofiles/schedulewidget.py:64 +msgid "Day:" +msgstr "天:" -#: ../../qt/app.py:796 -msgid "Home" -msgstr "家目錄" +#: qt/manageprofiles/schedulewidget.py:69 +msgid "Weekday:" +msgstr "工作日:" -#: ../../qt/app.py:811 -msgid "Backup folders" -msgstr "備份資料夾" +#: qt/manageprofiles/schedulewidget.py:74 +msgid "Time:" +msgstr "時間:" -#: ../../qt/app.py:942 -#, python-format -msgid "" -"Are you sure you want to remove the snapshot:\n" -"%s" -msgstr "" -"確認要刪除此快照:\n" -"%s" +#: qt/manageprofiles/schedulewidget.py:79 +msgid "Hours:" +msgstr "時:" -#: ../../qt/app.py:1025 -#, python-format -msgid "" -"Backup local files before overwriting or\n" -"removing with trailing '%(suffix)s'." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:87 +msgid "after the hour" +msgstr "小時後" + +#: qt/manageprofiles/schedulewidget.py:89 +msgid "Minutes:" +msgstr "分鐘:" -#: ../../qt/app.py:1028 ../../qt/settingsdialog.py:631 -#, python-format +#: qt/manageprofiles/schedulewidget.py:93 msgid "" -"Newer versions of files will be renamed with trailing '%(suffix)s' before " -"restoring.\n" -"If you don't need them anymore you can remove them with '%(cmd)s'" -msgstr "" +"Run Back In Time as soon as the drive is connected (only once every X days)." +" A sudo password prompt will appear." +msgstr "一旦驅動器連接,立即運行 Back In Time(每 X 天僅一次)。系統將提示您輸入 sudo 密碼。" -#: ../../qt/app.py:1038 +#: qt/manageprofiles/schedulewidget.py:98 msgid "" -"Only restore files which do not exist or\n" -"are newer than those in destination.\n" -"Using \"rsync --update\" option." -msgstr "" +"Run Back In Time repeatedly. This is useful if the computer is not running " +"regularly." +msgstr "反覆運行Back In Time。 如果計算機沒有正常運行,這非常有用。" -#: ../../qt/app.py:1071 -msgid "Remove newer files in original folder" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:110 +msgid "Every:" +msgstr "每天:" -#: ../../qt/app.py:1072 -msgid "" -"Restore selected files or folders to the original destination and\n" -"delete files/folders which are not in the snapshot.\n" -"This will delete files/folders which where excluded during taking the " -"snapshot!\n" -"Be extremely careful!!!" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:114 +msgid "Enable logging of debug messages" +msgstr "啟用偵錯訊息的日誌記錄" + +#: qt/manageprofiles/schedulewidget.py:118 +msgid "Writes debug-level messages into the system log via \"--debug\"." +msgstr "透過“--debug”將偵錯等級訊息寫入系統日誌。" -#: ../../qt/app.py:1083 -#, python-format +#: qt/manageprofiles/schedulewidget.py:120 msgid "" -"Do you really want to restore this files(s)\n" -"into new folder '%(path)s':" -msgstr "" +"Caution: Only use this temporarily for diagnostics, as it generates a large " +"amount of output." +msgstr "注意:僅臨時使用它進行診斷,因為它會產生大量輸出。" -#: ../../qt/app.py:1086 -msgid "Do you really want to restore this files(s):" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:145 +msgid "Disabled" +msgstr "關閉" -#: ../../qt/app.py:1098 -#, python-format -msgid "Are you sure you want to remove all newer files in '%(path)s'?" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:146 +msgid "At every boot/reboot" +msgstr "每次啟動/重新啟動時" + +#: qt/manageprofiles/schedulewidget.py:148 +#: qt/manageprofiles/schedulewidget.py:150 +#: qt/manageprofiles/schedulewidget.py:152 +#, python-brace-format +msgid "Every minute" +msgid_plural "Every {n} minutes" +msgstr[0] "每{n}分鐘" + +#: qt/manageprofiles/schedulewidget.py:154 +#: qt/manageprofiles/schedulewidget.py:156 +#: qt/manageprofiles/schedulewidget.py:158 +#: qt/manageprofiles/schedulewidget.py:160 +#: qt/manageprofiles/schedulewidget.py:162 +#, python-brace-format +msgid "Every hour" +msgid_plural "Every {n} hours" +msgstr[0] "每隔{n}小時" -#: ../../qt/app.py:1101 -msgid "" -"Are you sure you want to remove all newer files in your original folder?" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:163 +msgid "Custom hours" +msgstr "客製化時間" -#: ../../qt/app.py:1105 -msgid "" -"WARNING: deleting files in filesystem root could break your whole system!!!" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:164 +msgid "Every day" +msgstr "每天" -#: ../../qt/app.py:1307 -msgid "View the current disk content" -msgstr "檢視目前磁碟內容" +#: qt/manageprofiles/schedulewidget.py:165 +msgid "Repeatedly (anacron)" +msgstr "重複(anacron)" -#: ../../qt/app.py:1310 -#, python-format -msgid "Snapshot: %s" -msgstr "快照:%s" +#: qt/manageprofiles/schedulewidget.py:166 +msgid "When drive gets connected (udev)" +msgstr "當驅動器連接 (udev)" -#: ../../qt/app.py:1311 -#, python-format -msgid "View the snapshot made at %s" -msgstr "檢視於%s完成之快照" +#: qt/manageprofiles/schedulewidget.py:167 +msgid "Every week" +msgstr "每週" -#: ../../qt/app.py:1349 -#, python-format -msgid "Restore '%s'" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:168 +msgid "Every month" +msgstr "每月" -#: ../../qt/app.py:1350 -#, python-format -msgid "Restore '%s' to ..." -msgstr "" +#: qt/manageprofiles/schedulewidget.py:169 +msgid "Every year" +msgstr "每年" -#: ../../qt/app.py:1464 ../../qt/app.py:1481 -msgid "Authors" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:228 +msgid "Hour(s)" +msgstr "小時" -#: ../../qt/app.py:1465 ../../qt/app.py:1484 -msgid "Translations" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:229 +#: qt/manageprofiles/tab_remove_retention.py:271 +msgid "Day(s)" +msgstr "天" -#: ../../qt/app.py:1466 ../../qt/app.py:1487 -msgid "License" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:230 +#: qt/manageprofiles/tab_remove_retention.py:272 +msgid "Week(s)" +msgstr "週" -#: ../../qt/logviewdialog.py:57 -msgid "Last Log View" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:231 +msgid "Month(s)" +msgstr "月" -#: ../../qt/logviewdialog.py:59 -msgid "Snapshot Log View" -msgstr "" +#: qt/manageprofiles/schedulewidget.py:326 +msgid "" +"Custom hours can only be a comma separated list of hours (e.g. 8,12,18,23) " +"or */3 for periodic backups every 3 hours." +msgstr "自訂時間只能是逗號分隔的時間清單(例如 8,12,18,23)或 */3(每 3 小時定期備份一次)。" -#: ../../qt/logviewdialog.py:67 ../../qt/settingsdialog.py:67 -#: ../../qt/settingsdialog.py:294 ../../qt/settingsdialog.py:1968 -msgid "Profile:" -msgstr "設定組合:" +#: qt/manageprofiles/sshkeyselector.py:64 +msgid "'), + _('Choose an existing private key file from somewhere else.'), + icon.FOLDER + ) + + # generate key files + content_dict[self.ACT_ID_GENERATE_PAIR] = ( + _(''), + _('Create a new SSH key without passphrase.'), + icon.ADD + ) + + super().__init__( + parent=parent, + content_dict=content_dict + ) + + self._handlers = { + self.ACT_ID_SELECT_FILE: select_key_handler, + self.ACT_ID_GENERATE_PAIR: generate_pair_handler + } + + self.currentIndexChanged.connect(self._on_selection_changed) + + self._original_style = None + + @staticmethod + def _key_tooltip(path: Path) -> str: + return _('Full path: {path}').format(path=str(path)) + + def _on_selection_changed(self, _idx): + data = self.current_data + + try: + handler = self._handlers[data] + except KeyError: + return + + handler() + + def add_and_select(self, key_path: Path, is_invalid: bool = False): + """Add a new entry and select it after that.""" + + with qttools.block_signals(self): + + if self.has_data(key_path): + self.select_by_data(key_path) + + else: + if is_invalid: + self._add_on_top( + key_path, + 'Invalid or missing: ', + 'The key file is missing or somehow invalid.\n' + ) + else: + self._add_on_top(key_path) + + self.setCurrentIndex(0) + + self._fade_background() + + def _add_on_top(self, + key_path: Path, + prefix: str = '', + tooltip_prefix: str = ''): + # pylint: disable-next=import-outside-toplevel + import icon # noqa: PLC0415 + + self.insertItem( + 0, + icon.SSH_KEY_INVALID if prefix else icon.ENCRYPT, + f'{prefix}{key_path.name}', + userData=key_path + ) + self.setItemData( + 0, + SshKeyCombo._key_tooltip(f'{tooltip_prefix}{key_path}'), + Qt.ItemDataRole.ToolTipRole + ) + + def _fade_background(self, duration_ms=1200, steps=30): + palette = self.palette() + high = palette.color(QPalette.ColorRole.Highlight) + base = palette.color(QPalette.ColorRole.Base) + + # Helper vars for color interpolation + diff_r = high.red() - base.red() + diff_g = high.green() - base.green() + diff_b = high.blue() - base.blue() + + colors = [] + # base -> high + for curr_step in range(steps//2): + colors.append(QColor( + base.red() + diff_r * curr_step // steps, + base.green() + diff_g * curr_step // steps, + base.blue() + diff_b * curr_step // steps)) + + # Helper vars for color interpolation + diff_r = base.red() - high.red() + diff_g = base.green() - high.green() + diff_b = base.blue() - high.blue() + + # high -> base + for curr_step in range(steps//2, steps): + colors.append(QColor( + high.red() + diff_r * curr_step // steps, + high.green() + diff_g * curr_step // steps, + high.blue() + diff_b * curr_step // steps)) + + colors.append(None) + colors = deque(colors) + + interval = duration_ms // steps + self._original_style = self.styleSheet() + + def update_color(col): + if col is None: + self.setStyleSheet(self._original_style) + return + + self.setStyleSheet( + f'QComboBox {{ background-color: {col.name()}; }}') + + QTimer.singleShot( + interval, partial(update_color, colors.popleft())) + + update_color(colors.popleft()) + + +class SshKeySelector(QWidget): + """Main widget for selecting or generating key files""" + + def __init__(self, + parent: QWidget, + select_key_handler: Callable, + generate_pair_handler: Callable): + super().__init__(parent=parent) + + # radio: key selector + self.radio_key = QRadioButton(_('Private key:')) + self.selector = SshKeyCombo( + self, select_key_handler, generate_pair_handler) + + # radio: no key + self.radio_no = QRadioButton(_('Use system SSH configuration')) + tooltip = _( + 'Leaves the key file unselected. SSH connections will rely ' + 'on the system’s existing client configuration ' + '(e.g., ~/.ssh/config).') + qttools.set_wrapped_tooltip(self.radio_no, tooltip) + + # button group + self.btn_group = QButtonGroup(self) + self.btn_group.addButton(self.radio_no) + self.btn_group.addButton(self.radio_key) + + # layout + row_key = QHBoxLayout() + row_key.addWidget(self.radio_key, stretch=0) + row_key.addWidget(self.selector, stretch=1) + row_no = QHBoxLayout() + row_no.addWidget(self.radio_no) + layout = QVBoxLayout() + layout.addLayout(row_no) + layout.addLayout(row_key) + # zero margins + layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(layout) + + # events + self.btn_group.buttonClicked.connect(self._slot_clicked) + + # default state + self.radio_no.setChecked(True) + self._slot_clicked(self.radio_no) + + def _slot_clicked(self, button): + self.selector.setEnabled(button == self.radio_key) + + def add_and_select_key(self, key_path: Path): + """Enable the drop down widget and add and select a new entry to it.""" + self.selector.add_and_select(key_path) + self.radio_key.setChecked(True) + + def set_key(self, key_path: Path | None) -> None: + """Select an existing key based on its path. + + If not enabled this will also enable the drop down. If path is ``None`` + the drop down widget is disabled. + """ + + if key_path: + try: + self.selector.select_by_data(key_path) + except ValueError: + # Edge case that should not happen + if key_path.exists(): + logger.critical( + 'Undefined situation: Key path exists but is not ' + 'present in the key select widget. ' + f'{key_path=}' + ) + # Add this entry but mark it es invalid + self.selector.add_and_select(key_path, True) + + self.radio_key.setChecked(True) + self.btn_group.buttonClicked.emit(self.radio_key) + + else: + self.radio_no.setChecked(True) + self.btn_group.buttonClicked.emit(self.radio_no) + + def get_key(self) -> Path | None: + """Return the path of the current selected key or ``None`` if the + drop down widget is disabled.""" + if self.radio_no.isChecked(): + return None + + if isinstance(self.selector.current_data, Path): + return self.selector.current_data + + return None diff --git a/qt/manageprofiles/sshproxywidget.py b/qt/manageprofiles/sshproxywidget.py new file mode 100644 index 000000000..5ee462e32 --- /dev/null +++ b/qt/manageprofiles/sshproxywidget.py @@ -0,0 +1,115 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See file/folder LICENSE or go to +# . +"""A widget to setup an SSH proxy.""" +import getpass +from PyQt6.QtWidgets import (QVBoxLayout, + QHBoxLayout, + QWidget, + QLabel, + QLineEdit, + QCheckBox) +from PyQt6.QtCore import Qt + +import qttools + + +class SshProxyWidget(QWidget): + """Used in SSH snapshot profiles on the General tab. + + Dev note by buhtz (2024-04): Just a quick n dirty solution until the + re-design and re-factoring of the whole dialog. + """ + + def __init__(self, parent, host, port, user): + super().__init__(parent) + + if host == '': + port = '' + user = '' + + elif isinstance(port, int): + port = str(port) + + vlayout = QVBoxLayout(self) + # zero margins + vlayout.setContentsMargins(0, 0, 0, 0) + + self._checkbox = QCheckBox(_('SSH Proxy'), self) + vlayout.addWidget(self._checkbox) + self._checkbox.stateChanged.connect(self._slot_checkbox_changed) + + hlayout = QHBoxLayout() + vlayout.addLayout(hlayout) + + hlayout.addWidget(QLabel(_('Host:'), self)) + self.host_edit = QLineEdit(host, self) + hlayout.addWidget(self.host_edit) + + hlayout.addWidget(QLabel(_('Port:'), self)) + self.port_edit = QLineEdit(port, self) + hlayout.addWidget(self.port_edit) + + hlayout.addWidget(QLabel(_('User:'), self)) + self.user_edit = QLineEdit(user, self) + hlayout.addWidget(self.user_edit) + + if host == '': + self._disable() + + qttools.set_wrapped_tooltip( + self, + _('Connect to the target host via this proxy (also known as a ' + 'jump host). See "-J" in the "ssh" command documentation or ' + '"ProxyJump" in "ssh_config" man page for details.') + ) + + def _slot_checkbox_changed(self, state): + if Qt.CheckState(state) == Qt.CheckState.Checked: + self._enable() + else: + self._disable() + + def _set_default(self): + """Set GUI elements back to default.""" + self.host_edit.setText('') + self.port_edit.setText('22') + self.user_edit.setText(getpass.getuser()) + + def _disable(self): + self._set_default() + self._enable(False) + + def _enable(self, enable=True): + # QEdit and QLabel's + lay = self.layout().itemAt(1) + for idx in range(lay.count()): + lay.itemAt(idx).widget().setEnabled(enable) + + def values(self) -> dict[str, str, str]: + """The widgets values as a dict. + + Returns: + dict: A 3-item dict with keys "host", "port" and "user". + """ + if self._checkbox.isChecked(): + return { + 'host': self.host_edit.text(), + 'port': self.port_edit.text(), + 'user': self.user_edit.text(), + } + + return { + 'host': '', + 'port': '', + 'user': '', + } diff --git a/qt/manageprofiles/statebindcheckbox.py b/qt/manageprofiles/statebindcheckbox.py new file mode 100644 index 000000000..a4a91e9b1 --- /dev/null +++ b/qt/manageprofiles/statebindcheckbox.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module with an improved check box widget.""" +from PyQt6.QtWidgets import QCheckBox, QWidget + + +class StateBindCheckBox(QCheckBox): + """A check box binding other widgets enabled states to its own check state. + + If the check box is checked all bound widgets are enabled. If the box is + not checked all bound widgets are disabled. + """ + + def __init__(self, + text: str, + parent: QWidget, + bind: QWidget = None): + """ + Args: + text: Label for this check box. + parent: The parent widget. + bind: One or a list of widgets to bind. + """ + super().__init__(text=text, parent=parent) + + self._widgets = [] + + if bind: + self.bind(bind) + + self.stateChanged.connect(self._slot_state_changed) + + def bind(self, widget: QWidget) -> None: + """The enabled state of `widget` is bound to the check state of the + check box. + + Args: + widget: The widget to bind. + """ + self._widgets.append(widget) + self._slot_state_changed(self.isChecked()) + + def _slot_state_changed(self, state) -> None: + """Set the enabled state of each bound widget based on the check state + of the box.""" + for wdg in self._widgets: + wdg.setEnabled(state) diff --git a/qt/manageprofiles/storagesizewidget.py b/qt/manageprofiles/storagesizewidget.py new file mode 100644 index 000000000..7064f7712 --- /dev/null +++ b/qt/manageprofiles/storagesizewidget.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: © 2025 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See file/folder LICENSE or go to +# . +"""Module with a widget combining a spinbox and a combobox.""" +from PyQt6.QtWidgets import QWidget +from storagesize import StorageSize, SizeUnit +from event import Event +from manageprofiles.spinboxunit import SpinBoxWithUnit + + +class StorageSizeWidget(SpinBoxWithUnit): + """A combined widget for selected storage size values and their unit.""" + + def __init__(self, + parent: QWidget, + range_min_max: tuple[int, int], + value: StorageSize = StorageSize(0, SizeUnit.MIB)): + + content_dict = {unit: str(unit) for unit in SizeUnit} + del content_dict[SizeUnit.B] # exclude Bytes + + super().__init__( + parent=parent, + range_min_max=range_min_max, + content_dict=content_dict, + ) + + self._value = None + self.set_storagesize(value) + + self._combo.currentIndexChanged.connect(self._on_unit_changed) + self._spin.valueChanged.connect(self._on_spin_changed) + + self.event_value_changed = Event() + + def get_storagesize(self) -> StorageSize: + """Current value as StorageSize object.""" + val, unit = self.data_and_unit + + return StorageSize(val, unit) + + def set_storagesize(self, + value: StorageSize, + dont_touch_unit: bool = False): + """Set value using a StorageSize object.""" + if dont_touch_unit: + # copy + value = StorageSize(value.value(), value.unit) + # Use widgets unit + value.unit = self.unit() + + self.set_value(value.value()) + self.select_unit(value.unit) + + self._value = value + + def _on_spin_changed(self, val): + self._value.set_value(val) + + # Notify observers + self.event_value_changed.notify(self._value) + + def _on_unit_changed(self, _idx): + with self.event_value_changed.keep_silent(): + self._value.unit = self.unit() + self.set_value(self._value.value()) diff --git a/qt/manageprofiles/tab_exclude.py b/qt/manageprofiles/tab_exclude.py new file mode 100644 index 000000000..97859c140 --- /dev/null +++ b/qt/manageprofiles/tab_exclude.py @@ -0,0 +1,440 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# SPDX-FileCopyrightText: © 2025 Devin Black +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module about the Exclude tab""" +import copy +from PyQt6.QtWidgets import (QAbstractItemView, + QCheckBox, + QDialog, + QDialogButtonBox, + QHBoxLayout, + QHeaderView, + QLabel, + QLineEdit, + QPushButton, + QSpinBox, + QTreeWidget, + QTreeWidgetItem, + QVBoxLayout, + QWidget) +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QPalette, QBrush +import tools +import qttools +from qttools import custom_sort_order +from filedialog import FileDialog +from bitwidgets import HypertextLabel +from manageprofiles.excludesuggestions import (ExcludeSuggestionsDialog, + EXCLUDE_SUGGESTIONS, + get_default_excludes) + +MATCH_FLAGS = Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchCaseSensitive + + +class ExcludeTab(QWidget): + """Create the 'Exclude' tab.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent): # noqa: PLR0915 + # pylint: disable=too-many-statements + super().__init__(parent=parent) + + self._parent_dialog = parent + self.icon = parent.icon + self.config = parent.config + + # Snapshot mode + self.mode = None + + layout = QVBoxLayout(self) + + self.lbl_ssh_encfs_exclude_warning = QLabel(_( + "{BOLD}Info{ENDBOLD}: " + "In 'SSH encrypted' mode, only single or double asterisks are " + "functional (e.g. {example2}). Other types of wildcards and " + "patterns will be ignored (e.g. {example1}). Filenames are " + "unpredictable in this mode due to encryption by EncFS.").format( + BOLD='', + ENDBOLD='', + example1="'foo*', " + "'[fF]oo', " + "'fo?'", + example2="'foo/*', " + "'foo/**/bar'" + ), + self + ) + self.lbl_ssh_encfs_exclude_warning.setWordWrap(True) + layout.addWidget(self.lbl_ssh_encfs_exclude_warning) + + self.list_exclude = QTreeWidget(self) + self.list_exclude.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection) + self.list_exclude.setRootIsDecorated(False) + self.list_exclude.setHeaderLabels( + [_('Exclude patterns, files or directories'), 'Count']) + + self.list_exclude.header().setSectionResizeMode( + 0, QHeaderView.ResizeMode.Stretch) + self.list_exclude.header().setSectionsClickable(True) + self.list_exclude.header().setSortIndicatorShown(True) + self.list_exclude.header().setSectionHidden(1, True) + self.list_exclude_sort_loop = False + self.list_exclude.header().sortIndicatorChanged \ + .connect(self._exclude_custom_sort_order) + + layout.addWidget(self.list_exclude) + + buttons_layout = QHBoxLayout() + layout.addLayout(buttons_layout) + + self.btn_exclude_add = QPushButton( + self.icon.ADD, _('Add pattern'), self) + buttons_layout.addWidget(self.btn_exclude_add) + self.btn_exclude_add.clicked.connect(self.btn_exclude_add_clicked) + + self.btn_exclude_file = QPushButton( + self.icon.ADD, _('Add files'), self) + buttons_layout.addWidget(self.btn_exclude_file) + self.btn_exclude_file.clicked.connect(self.btn_exclude_file_clicked) + + self.btn_exclude_folder = QPushButton( + self.icon.ADD, _('Add directories'), self) + buttons_layout.addWidget(self.btn_exclude_folder) + self.btn_exclude_folder.clicked.connect( + self.btn_exclude_folder_clicked) + + self.btn_suggestions = QPushButton( + self.icon.DEFAULT_EXCLUDE, _('Suggestions'), self) + self.btn_suggestions.setToolTip(_( + 'Select from common used items to add to exclude list.')) + buttons_layout.addWidget(self.btn_suggestions) + self.btn_suggestions.clicked.connect(self.btn_suggestions_clicked) + + self.btn_exclude_remove = QPushButton( + self.icon.REMOVE, _('Remove'), self) + buttons_layout.addWidget(self.btn_exclude_remove) + self.btn_exclude_remove.clicked.connect( + self.btn_exclude_remove_clicked) + + # exclude files by size + hlayout = QHBoxLayout() + layout.addLayout(hlayout) + self.cb_exclude_by_size = QCheckBox( + _('Exclude files bigger than:'), self) + qttools.set_wrapped_tooltip( + self.cb_exclude_by_size, + [ + _('Exclude files bigger than value in {size_unit}.') + .format(size_unit='MiB'), + _("With 'Full rsync mode' disabled, this setting affects only " + "newly created files, as rsync treats it as transfer option " + "rather than an exclusion rule. Consequently, large files " + "that have already been backed up will remain in backups " + "even if they are modified.") + ] + ) + hlayout.addWidget(self.cb_exclude_by_size) + self.spb_exclude_by_size = QSpinBox(self) + self.spb_exclude_by_size.setSuffix(' MiB') + self.spb_exclude_by_size.setRange(0, 100000000) + hlayout.addWidget(self.spb_exclude_by_size) + hlayout.addStretch() + # pylint: disable-next=unnecessary-lambda-assignment,unnecessary-lambda + enabled = lambda state: \ + self.spb_exclude_by_size.setEnabled(state) # noqa + enabled(False) + self.cb_exclude_by_size.stateChanged.connect(enabled) + + def load_values(self, profile_state): + """Load config values into the GUI""" + + self.list_exclude.clear() + + for exclude in self.config.exclude(): + self._add_exclude_pattern(exclude) + + # add defaults if it is a fresh profile + if self.config.is_current_profile_unsaved(): + for exclude in get_default_excludes(): + self._add_exclude_pattern(exclude) + + self.cb_exclude_by_size.setChecked(self.config.excludeBySizeEnabled()) + self.spb_exclude_by_size.setValue(self.config.excludeBySize()) + + try: + excl_sort = profile_state.exclude_sorting + self.list_exclude.sortItems( + excl_sort[0], Qt.SortOrder(excl_sort[1]) + ) + except KeyError: + pass + + def store_values(self, profile_state): + """Store values from GUI into the config""" + + # exclude patterns + profile_state.exclude_sorting = ( + self.list_exclude.header().sortIndicatorSection(), + self.list_exclude.header().sortIndicatorOrder().value + ) + # Sort (optional: replicates original behavior) + self.list_exclude.sortItems(1, Qt.SortOrder.AscendingOrder) + + # Store exclude list + exclude_list = [] + for index in range(self.list_exclude.topLevelItemCount()): + item = self.list_exclude.topLevelItem(index) + exclude_list.append(item.text(0)) + self.config.setExclude(exclude_list) + + # Store "exclude by size" settings + self.config.setExcludeBySize( + self.cb_exclude_by_size.isChecked(), + self.spb_exclude_by_size.value() + ) + + return True + + def _add_exclude_pattern(self, pattern): + item = QTreeWidgetItem() + item.setText(0, pattern) + item.setData(0, Qt.ItemDataRole.UserRole, pattern) + self._format_exclude_item(item) + + # Add item to the widget + self.list_exclude.addTopLevelItem(item) + + return item + + def btn_exclude_remove_clicked(self): + """Handle button click""" + + for item in self.list_exclude.selectedItems(): + index = self.list_exclude.indexOfTopLevelItem(item) + if index < 0: + continue + + # means removing item at this index + self.list_exclude.takeTopLevelItem(index) + + if self.list_exclude.topLevelItemCount() > 0: + self.list_exclude.setCurrentItem(self.list_exclude.topLevelItem(0)) + + def add_exclude(self, pattern): + """Initiate adding a new exclude pattern to the list widget. + + See `_add_exclude_pattern()` also. + """ + if not pattern: + return + + # Duplicate? + duplicates = self.list_exclude.findItems(pattern, MATCH_FLAGS) + + if duplicates: + self.list_exclude.setCurrentItem(duplicates[0]) + return + + # Create new entry and add it to the list widget. + item = self._add_exclude_pattern(pattern) + + # Select/highlight that entry. + self.list_exclude.setCurrentItem(item) + + def btn_exclude_add_clicked(self): + """Handle button click + + Dev note (buhtz, 2025-10): Feature idea for later versions. Use rsync + --dry-run with --debug=FILTER to see include/exclude decisions. Show + them life as preview in the pattern input dialog, for a specific file. + Extend this feature to show all include and exclude matches (#734). + """ + + dlg = QDialog(self) + dlg.setWindowTitle(_('Exclude pattern')) + layout = QVBoxLayout(dlg) + layout.addWidget(QLabel(_('Enter an exclude pattern:'))) + line_edit = QLineEdit() + layout.addWidget(line_edit) + label_help = HypertextLabel( + label=_( + 'For help, see the rsync man page section {link}.' + ).format(link='PATTERN MATCHING RULES'), + link_slot=self._slot_rsync_pattern_match_link, + link_tooltip=_('Open rsync man page') + ) + layout.addWidget(label_help) + buttons = QDialogButtonBox( + QDialogButtonBox.StandardButton.Ok + | QDialogButtonBox.StandardButton.Cancel) + buttons.accepted.connect(dlg.accept) + buttons.rejected.connect(dlg.reject) + layout.addWidget(buttons) + + if not dlg.exec(): + return + + pattern = line_edit.text().strip() + + if not pattern: + return + + self.add_exclude(pattern) + + def _slot_rsync_pattern_match_link(self): + qttools.open_man_page('rsync', section='PATTERN MATCHING RULES') + + def btn_exclude_file_clicked(self): + """Handle button click""" + + dlg = FileDialog( + parent=self, + title=_('Exclude files'), + show_hidden=True, + allow_multiselection=True, + dirs_only=False) + + for path in dlg.result(): + self.add_exclude(str(path)) + + def btn_exclude_folder_clicked(self): + """Handle button click""" + + # pylint: disable=duplicate-code + dlg = FileDialog(parent=self, + title=_('Exclude directories'), + show_hidden=True, + allow_multiselection=True, + dirs_only=True) + dirs = dlg.result() + + for path in dirs: + self.add_exclude(str(path)) + + def _sync_suggestions_check_state(self): + """Sync the check state of the suggestions list with the current + exclude liste. + + The check state of suggested exclude items is modified based on the + content of the exclude list. Items still in the exclude list are + checked and all other are not. + """ + content = copy.deepcopy(EXCLUDE_SUGGESTIONS) # dict + + # flat list of suggestions + suggestions = [ + first_item + for group in content.values() + for first_item, *_ in group + ] + + # remove suggestions not existent in current exclude list + for entry in suggestions[:]: + if not self.list_exclude.findItems(entry, MATCH_FLAGS): + suggestions.remove(entry) + + # Modify check state of suggested includes + for group, entries in content.items(): + for idx, entry in enumerate(entries): + check = entry[0] in suggestions + content[group][idx][-1] = check + + return content + + def btn_suggestions_clicked(self): + """Handle button click""" + content = self._sync_suggestions_check_state() + + dlg = ExcludeSuggestionsDialog(self, content) + answer = dlg.exec() + + if answer == QDialog.DialogCode.Rejected: + return + + checked, unchecked = dlg.get_checked_and_unchecked() + + for entry in checked: + self.add_exclude(entry) + + self._remove_entries(unchecked) + + def _remove_entries(self, entries: list[str]) -> None: + """Remove entries from the exclude list if existent.""" + for entry in entries: + # find item + items = self.list_exclude.findItems(entry, MATCH_FLAGS) + if items: + # remove it + idx = self.list_exclude.indexOfTopLevelItem(items[0]) + self.list_exclude.takeTopLevelItem(idx) + + def update_exclude_items(self): + """Used by parent dialog when profile mode was changed.""" + for index in range(self.list_exclude.topLevelItemCount()): + item = self.list_exclude.topLevelItem(index) + self._format_exclude_item(item) + + def _format_exclude_item_encfs_invalid(self, item): + """Modify visual appearance of an item in the exclude list widget to + express that the item is invalid. + + See :py:func:`_format_exclude_item` for details. + """ + # Icon + item.setIcon(0, self.icon.INVALID_EXCLUDE) + + # ToolTip + item.setData( + 0, + Qt.ItemDataRole.ToolTipRole, + _("Disabled because this pattern is not functional in " + "mode 'SSH encrypted'.") + ) + + # Fore- and Backgroundcolor (as disabled) + item.setBackground(0, QPalette().brush(QPalette.ColorGroup.Disabled, + QPalette.ColorRole.Window)) + item.setForeground(0, QPalette().brush(QPalette.ColorGroup.Disabled, + QPalette.ColorRole.Text)) + + def _format_exclude_item(self, item): + """Modify visual appearance of an item in the exclude list widget. + + Dev note (2025-12, buhtz): Why not using simple file/dir icons? + """ + if (self.mode == 'ssh_encfs' + and tools.patternHasNotEncryptableWildcard(item.text(0))): + # Invalid item (because of encfs restrictions) + self._format_exclude_item_encfs_invalid(item) + + else: + # default background color + item.setBackground(0, QBrush()) + item.setForeground(0, QBrush()) + + # Remove items tooltip + item.setData(0, Qt.ItemDataRole.ToolTipRole, None) + + # # Icon: default exclude item + # if item.text(0) in self.config.DEFAULT_EXCLUDE: + # item.setIcon(0, self.icon.DEFAULT_EXCLUDE) + + # else: + + # Icon: user defined + item.setIcon(0, self.icon.EXCLUDE) + + def _exclude_custom_sort_order(self, *args): + self.list_exclude_sort_loop = custom_sort_order( + self.list_exclude.header(), self.list_exclude_sort_loop, *args) diff --git a/qt/manageprofiles/tab_expert_options.py b/qt/manageprofiles/tab_expert_options.py new file mode 100644 index 000000000..fcdf16d23 --- /dev/null +++ b/qt/manageprofiles/tab_expert_options.py @@ -0,0 +1,431 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""About Export Options tab""" +from PyQt6.QtWidgets import (QDialog, + QVBoxLayout, + QHBoxLayout, + QGridLayout, + QLabel, + QSpinBox, + QLineEdit, + QCheckBox) +import tools +from config import Config +import qttools +import messagebox +from manageprofiles.statebindcheckbox import StateBindCheckBox +from manageprofiles.copylinkswidget import CopySymlinksWidget +from bitwidgets import HLineWidget + + +class ExpertOptionsTab(QDialog): + """The 'Expert Options' tab in the Manage Profiles dialog.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent): # noqa: PLR0915 + # pylint: disable=too-many-statements + super().__init__(parent=parent) + + self._parent_dialog = parent + + tab_layout = QVBoxLayout(self) + + # --- initial warning --- + txt = _( + 'These options are for advanced configurations. Modify ' + 'only if fully aware of their implications.' + ) + label = qttools.create_warning_label(txt) + tab_layout.addWidget(label) + tab_layout.addWidget(HLineWidget()) + + # --- rsync with nice --- + tab_layout.addWidget(QLabel( + _("Run 'rsync' with '{cmd}':").format(cmd='nice'))) + + grid = QGridLayout() + grid.setColumnMinimumWidth(0, 20) # left indent + tab_layout.addLayout(grid) + + self._cb_nice_on_cron = QCheckBox( + _('as cron job') + + self._default_string(self.config.DEFAULT_RUN_NICE_FROM_CRON), + self) + grid.addWidget(self._cb_nice_on_cron, 0, 1) + + self._cb_nice_on_remote = QCheckBox( + _('on remote host') + + self._default_string(self.config.DEFAULT_RUN_NICE_ON_REMOTE), + self) + grid.addWidget(self._cb_nice_on_remote, 1, 1) + + # --- rsync with ionice --- + tab_layout.addWidget(QLabel( + _("Run 'rsync' with '{cmd}':").format(cmd='ionice'))) + grid = QGridLayout() + grid.setColumnMinimumWidth(0, 20) + tab_layout.addLayout(grid) + + self._cb_ionice_on_cron = QCheckBox( + _('as cron job') + + self._default_string(self.config.DEFAULT_RUN_IONICE_FROM_CRON), + self) + grid.addWidget(self._cb_ionice_on_cron, 0, 1) + + self._cb_ionice_on_user = QCheckBox( + _('when taking a manual backup') + + self._default_string(self.config.DEFAULT_RUN_IONICE_FROM_USER), + self) + grid.addWidget(self._cb_ionice_on_user, 1, 1) + + self._cb_ionice_on_remote = QCheckBox( + _('on remote host') + + self._default_string(self.config.DEFAULT_RUN_IONICE_ON_REMOTE), + self) + grid.addWidget(self._cb_ionice_on_remote, 2, 1) + + # --- rsync with nocache --- + tab_layout.addWidget(QLabel( + _("Run 'rsync' with '{cmd}':").format(cmd='nocache'))) + + grid = QGridLayout() + grid.setColumnMinimumWidth(0, 20) + tab_layout.addLayout(grid) + + nocache_available = tools.checkCommand('nocache') + if not nocache_available: + grid.addWidget( + QLabel( + '' + + _("Please install 'nocache' to enable this option.") + + ''), + 0, + 1) + + self._cb_nocache_on_local = QCheckBox( + _('on local machine') + + self._default_string(self.config.DEFAULT_RUN_NOCACHE_ON_LOCAL), + self) + grid.addWidget(self._cb_nocache_on_local, 1, 1) + self._cb_nocache_on_local.setEnabled(nocache_available) + + self._cb_nocache_on_remote = QCheckBox( + _('on remote host') + + self._default_string(self.config.DEFAULT_RUN_NOCACHE_ON_REMOTE), + self) + grid.addWidget(self._cb_nocache_on_remote, 2, 1) + + # --- redirect output --- + self._cb_redirect_stdout_cron = QCheckBox( + _('Redirect stdout to /dev/null in cronjobs.') + + self._default_string( + self.config.DEFAULT_REDIRECT_STDOUT_IN_CRON), + self) + qttools.set_wrapped_tooltip( + self._cb_redirect_stdout_cron, + _('Cron will automatically send an email with attached output ' + 'of cronjobs if an MTA is installed.') + ) + tab_layout.addWidget(self._cb_redirect_stdout_cron) + + self._cb_redirect_stderr_cron = QCheckBox( + _('Redirect stderr to /dev/null in cronjobs.') + + self._default_string( + self.config.DEFAULT_REDIRECT_STDERR_IN_CRON), + self) + qttools.set_wrapped_tooltip( + self._cb_redirect_stderr_cron, + _('Cron will automatically send an email with attached errors ' + 'of cronjobs if an MTA is installed.') + ) + tab_layout.addWidget(self._cb_redirect_stderr_cron) + + # bandwidth limit + hlayout = QHBoxLayout() + tab_layout.addLayout(hlayout) + + self._spb_bwlimit = QSpinBox(self) + self._spb_bwlimit.setSuffix(' ' + _('KB/sec')) + self._spb_bwlimit.setSingleStep(100) + self._spb_bwlimit.setRange(0, 1000000) + + self._cb_bwlimit = StateBindCheckBox( + _('Limit rsync bandwidth usage:'), self, self._spb_bwlimit) + hlayout.addWidget(self._cb_bwlimit) + hlayout.addWidget(self._spb_bwlimit) + hlayout.addStretch() + + qttools.set_wrapped_tooltip( + self._cb_bwlimit, + [ + "Uses 'rsync --bwlimit=RATE'. From 'man rsync':", + 'This option allows you to specify the maximum transfer rate ' + 'for the data sent over the socket, specified in units per ' + 'second. The RATE value can be suffixed with a string to ' + 'indicate a size multiplier, and may be a fractional value ' + '(e.g. "--bwlimit=1.5m").', + 'If no suffix is specified, the value will be assumed to be ' + 'in units of 1024 bytes (as if "K" or "KiB" had been ' + 'appended).', + 'See the --max-size option for a description of all the ' + 'available suffixes. A value of zero specifies no limit.' + '', + 'For backward-compatibility reasons, the rate limit will be ' + 'rounded to the nearest KiB unit, so no rate smaller than ' + '1024 bytes per second is possible.', + '', + 'Rsync writes data over the socket in blocks, and this option ' + 'both limits the size of the blocks that rsync writes, and ' + 'tries to keep the average transfer rate at the requested ' + 'limit. Some "burstiness" may be seen where rsync writes out ' + 'a block of data and then sleeps to bring the average rate ' + 'into compliance.', + '', + 'Due to the internal buffering of data, the --progress ' + 'option may not be an accurate reflection on how fast the ' + 'data is being sent. This is because some files can show up ' + 'as being rapidly sent when the data is quickly buffered, ' + 'while other can show up as very slow when the flushing of ' + 'the output buffer occurs. This may be fixed in a future ' + 'version.' + ] + ) + + self._cb_preserve_acl = QCheckBox(_('Preserve ACL'), self) + qttools.set_wrapped_tooltip( + self._cb_preserve_acl, + [ + "Uses 'rsync -A'. From 'man rsync':", + 'This option causes rsync to update the destination ACLs to ' + 'be the same as the source ACLs. The option also implies ' + '--perms.', + '', + 'The source and destination systems must have compatible ACL ' + 'entries for this option to work properly. See the ' + '--fake-super option for a way to backup and restore ACLs ' + 'that are not compatible.' + ] + ) + tab_layout.addWidget(self._cb_preserve_acl) + + self._cb_preserve_xattr = QCheckBox( + _('Preserve extended attributes (xattr)'), self) + qttools.set_wrapped_tooltip( + self._cb_preserve_xattr, + [ + "Uses 'rsync -X'. From 'man rsync':", + 'This option causes rsync to update the destination extended ' + 'attributes to be the same as the source ones.', + '', + 'For systems that support extended-attribute namespaces, a ' + 'copy being done by a super-user copies all namespaces ' + 'except system.*. A normal user only copies the user.* ' + 'namespace. To be able to backup and restore non-user ' + 'namespaces as a normal user, see the --fake-super option.', + '', + 'Note that this option does not copy rsyncs special xattr ' + 'values (e.g. those used by --fake-super) unless you repeat ' + 'the option (e.g. -XX). This "copy all xattrs" mode cannot be ' + 'used with --fake-super.' + ] + ) + tab_layout.addWidget(self._cb_preserve_xattr) + + self._wdg_copy_links = CopySymlinksWidget(self) + tab_layout.addWidget(self._wdg_copy_links) + + # one file system option + self._cb_one_filesystem = QCheckBox( + _('Restrict to one file system'), self) + qttools.set_wrapped_tooltip( + self._cb_one_filesystem, + [ + "Uses 'rsync --one-file-system'. From 'man rsync':", + 'This tells rsync to avoid crossing a filesystem boundary ' + 'when recursing. This does not limit the user\'s ability ' + 'to specify items to copy from multiple filesystems, just ' + 'rsync\'s recursion through the hierarchy of each directory ' + 'that the user specified, and also the analogous recursion ' + 'on the receiving side during deletion. Also keep in mind ' + 'that rsync treats a "bind" mount to the same device as ' + 'being on the same filesystem.' + ] + ) + tab_layout.addWidget(self._cb_one_filesystem) + + # additional rsync options + tooltip = _('Options must be quoted e.g. {example}.').format( + example='--exclude-from="/path/to/my exclude file"') + + self._txt_rsync_options = QLineEdit(self) + self._txt_rsync_options.editingFinished.connect( + self._slot_rsync_options_editing_finished) + self._txt_rsync_options.setToolTip(tooltip) + + self._cb_rsync_options = StateBindCheckBox( + _('Paste additional options to rsync'), + self, + self._txt_rsync_options) + + self._cb_rsync_options.setToolTip(tooltip) + + # ssh prefix + + rsync_options_value = '--rsync-path="FOO=bar:\\$FOO /usr/bin/rsync"' + tooltip = [ + _('Prefix to run before every command on remote host.'), + _("Variables need to be escaped with \\$FOO. This doesn't touch " + 'rsync. So to add a prefix for rsync use "{example_value}" with ' + '{rsync_options_value}.').format( + example_value=self._cb_rsync_options.text(), + rsync_options_value=rsync_options_value), + '', + _('default') + ': ' + self.config.DEFAULT_SSH_PREFIX + ] + self._txt_ssh_prefix = QLineEdit(self) + qttools.set_wrapped_tooltip(self._txt_ssh_prefix, tooltip) + self._cb_ssh_prefix = StateBindCheckBox( + _('Add prefix to SSH commands'), self, self._txt_ssh_prefix) + qttools.set_wrapped_tooltip(self._cb_ssh_prefix, tooltip) + + sub_grid = QGridLayout() + sub_grid.addWidget(self._cb_rsync_options, 0, 0) + sub_grid.addWidget(self._txt_rsync_options, 0, 1) + sub_grid.addWidget(self._cb_ssh_prefix, 1, 0) + sub_grid.addWidget(self._txt_ssh_prefix, 1, 1) + tab_layout.addLayout(sub_grid) + + self._cb_ssh_ping = QCheckBox(_('Check if remote host is online')) + qttools.set_wrapped_tooltip( + self._cb_ssh_ping, + _('Warning: If disabled and the remote host is not available, ' + 'this could lead to some weird errors.') + ) + self._cb_ssh_check_commands = QCheckBox( + _('Check if remote host supports all necessary commands.')) + qttools.set_wrapped_tooltip( + self._cb_ssh_check_commands, + _('Warning: If disabled and the remote host does not support all ' + 'necessary commands, this could lead to some weird errors.') + ) + tab_layout.addWidget(self._cb_ssh_ping) + tab_layout.addWidget(self._cb_ssh_check_commands) + + tab_layout.addStretch() + + @property + def config(self) -> Config: + """The config instance.""" + return self._parent_dialog.config + + def _default_string(self, value: bool) -> str: + return ' ' + _('(default: {})').format( + _('enabled') if value else _('disabled')) + + def load_values(self): + """Load config values into the GUI""" + + self._cb_nice_on_cron.setChecked(self.config.niceOnCron()) + self._cb_ionice_on_cron.setChecked(self.config.ioniceOnCron()) + self._cb_ionice_on_user.setChecked(self.config.ioniceOnUser()) + self._cb_nice_on_remote.setChecked(self.config.niceOnRemote()) + self._cb_ionice_on_remote.setChecked(self.config.ioniceOnRemote()) + self._cb_nocache_on_local.setChecked( + self.config.nocacheOnLocal() + and self._cb_nocache_on_local.isEnabled()) + self._cb_nocache_on_remote.setChecked(self.config.nocacheOnRemote()) + self._cb_redirect_stdout_cron.setChecked( + self.config.redirectStdoutInCron()) + self._cb_redirect_stderr_cron.setChecked( + self.config.redirectStderrInCron()) + self._cb_bwlimit.setChecked(self.config.bwlimitEnabled()) + self._spb_bwlimit.setValue(self.config.bwlimit()) + self._cb_preserve_acl.setChecked(self.config.preserveAcl()) + self._cb_preserve_xattr.setChecked(self.config.preserveXattr()) + + all_links = self.config.copyLinks() + only_external = self.config.copyUnsafeLinks() + self._wdg_copy_links.set_values(all_links, only_external) + + self._cb_one_filesystem.setChecked(self.config.oneFileSystem()) + self._cb_rsync_options.setChecked(self.config.rsyncOptionsEnabled()) + self._txt_rsync_options.setText(self.config.rsyncOptions()) + self._cb_ssh_prefix.setChecked(self.config.sshPrefixEnabled()) + self._txt_ssh_prefix.setText(self.config.sshPrefix()) + self._cb_ssh_ping.setChecked(self.config.sshCheckPingHost()) + self._cb_ssh_check_commands.setChecked(self.config.sshCheckCommands()) + + def store_values(self): + """Store values from GUI into the config""" + + self.config.setNiceOnCron(self._cb_nice_on_cron.isChecked()) + self.config.setIoniceOnCron(self._cb_ionice_on_cron.isChecked()) + self.config.setIoniceOnUser(self._cb_ionice_on_user.isChecked()) + self.config.setNiceOnRemote(self._cb_nice_on_remote.isChecked()) + self.config.setIoniceOnRemote(self._cb_ionice_on_remote.isChecked()) + self.config.setNocacheOnLocal(self._cb_nocache_on_local.isChecked()) + self.config.setNocacheOnRemote(self._cb_nocache_on_remote.isChecked()) + self.config.setRedirectStdoutInCron( + self._cb_redirect_stdout_cron.isChecked()) + self.config.setRedirectStderrInCron( + self._cb_redirect_stderr_cron.isChecked()) + self.config.setBwlimit(self._cb_bwlimit.isChecked(), + self._spb_bwlimit.value()) + self.config.setPreserveAcl(self._cb_preserve_acl.isChecked()) + self.config.setPreserveXattr(self._cb_preserve_xattr.isChecked()) + + self.config.setCopyLinks(self._wdg_copy_links.all_links) + self.config.setCopyUnsafeLinks( + self._wdg_copy_links.only_external_links) + + self.config.setOneFileSystem(self._cb_one_filesystem.isChecked()) + self.config.setRsyncOptions(self._cb_rsync_options.isChecked(), + self._txt_rsync_options.text()) + self.config.setSshPrefix(self._cb_ssh_prefix.isChecked(), + self._txt_ssh_prefix.text()) + self.config.setSshCheckPingHost(self._cb_ssh_ping.isChecked()) + self.config.setSshCheckCommands( + self._cb_ssh_check_commands.isChecked()) + + def update_items_state(self, enabled: bool): + """Update state of widgets based on changed profile mode.""" + self._cb_nice_on_remote.setEnabled(enabled) + self._cb_ionice_on_remote.setEnabled(enabled) + self._cb_nocache_on_remote.setEnabled(enabled) + self._cb_ssh_prefix.setVisible(enabled) + self._txt_ssh_prefix.setVisible(enabled) + self._cb_ssh_ping.setVisible(enabled) + self._cb_ssh_check_commands.setVisible(enabled) + + def _slot_rsync_options_editing_finished(self): + """When editing the rsync options is finished warn and remove + --old-args option if present. + """ + txt = self._txt_rsync_options.text() + + if '--old-args' in txt: + # No translation for this message because it is a rare case. + messagebox.warning( + text='Found rsync flag "--old-args". That flag will be removed' + ' from the options because it conflicts with the flag "-s" ' + '(also known as "--secluded-args" or "--protected-args") which' + ' is used by Back In Time to force the "new form of argument ' + 'protection" in rsync.', + widget_to_center_on=self + ) + + # Don't leave two-blank spaces between other arguments + txt = txt.replace('--old-args ', '') + txt = txt.replace(' --old-args', '') + txt = txt.replace('--old-args', '') + self._txt_rsync_options.setText(txt) diff --git a/qt/manageprofiles/tab_general.py b/qt/manageprofiles/tab_general.py new file mode 100644 index 000000000..91ce17eaf --- /dev/null +++ b/qt/manageprofiles/tab_general.py @@ -0,0 +1,893 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module about the General tab""" +import os +from pathlib import Path +from typing import Any +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import (QCheckBox, + QDialog, + QGridLayout, + QGroupBox, + QHBoxLayout, + QLabel, + QLineEdit, + QToolButton, + QVBoxLayout) +from config import Config +import tools +import logger +import sshtools +from exceptions import MountException, NoPubKeyLogin, KnownHost +import mount +from bitbase import URL_ENCRYPT_TRANSITION, DIR_SSH_KEYS +import version +import schedule +import qttools +import messagebox +# from statedata import StateData +from manageprofiles import combobox +from manageprofiles import schedulewidget +from manageprofiles.sshproxywidget import SshProxyWidget +from manageprofiles.sshkeyselector import SshKeySelector +from bitwidgets import HLineWidget +from filedialog import FileDialog + + +class GeneralTab(QDialog): + """Create the 'Generals' tab.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent): # noqa: PLR0915 + # pylint: disable=too-many-statements + super().__init__(parent=parent) + + self._parent_dialog = parent + + tab_layout = QVBoxLayout(self) + + # Snapshot mode + self.mode = None + + vlayout = QVBoxLayout() + tab_layout.addLayout(vlayout) + + self._combo_modes = self._snapshot_mode_combobox() + hlayout = QHBoxLayout() + hlayout.addWidget(QLabel(_('Mode:'), self)) + hlayout.addWidget(self._combo_modes, 1) + vlayout.addLayout(hlayout) + + # EncFS deprecation (#1734, #1735) + self._lbl_encfs_warning = self._create_label_encfs_deprecation() + tab_layout.addWidget(self._lbl_encfs_warning) + tab_layout.addWidget(HLineWidget()) + + # Where to save snapshots + group_box = QGroupBox(self) + self._group_mode_local = group_box + group_box.setTitle(_('Where to save backups')) + tab_layout.addWidget(group_box) + + vlayout = QVBoxLayout(group_box) + + hlayout = QHBoxLayout() + vlayout.addLayout(hlayout) + + self._edit_backup_path = QLineEdit(self) + self._edit_backup_path.setReadOnly(True) + self._edit_backup_path.textChanged.connect( + self._slot_full_path_changed) + hlayout.addWidget(self._edit_backup_path) + + self._btn_backup_path = QToolButton(self) + self._btn_backup_path.setToolButtonStyle( + Qt.ToolButtonStyle.ToolButtonIconOnly) + self._btn_backup_path.setIcon(self.icon.FOLDER) + self._btn_backup_path.setMinimumSize(32, 28) + hlayout.addWidget(self._btn_backup_path) + self._btn_backup_path.clicked.connect( + self._slot_snapshots_path_clicked) + + # --- SSH --- + group_box = QGroupBox(self) + self._group_mode_ssh = group_box + group_box.setTitle(_('SSH Settings')) + tab_layout.addWidget(group_box) + + vlayout = QVBoxLayout(group_box) + + hlayout1 = QHBoxLayout() + vlayout.addLayout(hlayout1) + hlayout2 = QHBoxLayout() + vlayout.addLayout(hlayout2) + # hlayout3 = QHBoxLayout() + # vlayout.addLayout(hlayout3) + + self._lbl_ssh_host = QLabel(_('Host:'), self) + hlayout1.addWidget(self._lbl_ssh_host) + self._txt_ssh_host = QLineEdit(self) + hlayout1.addWidget(self._txt_ssh_host) + + self._lbl_ssh_port = QLabel(_('Port:'), self) + hlayout1.addWidget(self._lbl_ssh_port) + self._txt_ssh_port = QLineEdit(self) + hlayout1.addWidget(self._txt_ssh_port) + + self._lbl_ssh_user = QLabel(_('User:'), self) + hlayout1.addWidget(self._lbl_ssh_user) + self._txt_ssh_user = QLineEdit(self) + hlayout1.addWidget(self._txt_ssh_user) + + self._lbl_ssh_path = QLabel(_('Path:'), self) + hlayout2.addWidget(self._lbl_ssh_path) + self._txt_ssh_path = QLineEdit(self) + self._txt_ssh_path.textChanged.connect(self._slot_full_path_changed) + hlayout2.addWidget(self._txt_ssh_path) + + group_box = QGroupBox(self) + group_box.setTitle(_('Key file:')) + group_layout = QVBoxLayout() + group_box.setLayout(group_layout) + self.key_selector = SshKeySelector( + self, + self._slot_ssh_private_key_file_clicked, + self._slot_ssh_key_gen_clicked + ) + group_layout.addWidget(self.key_selector) + vlayout.addWidget(group_box) + + # Align the width of that three labels + width = max( + self._lbl_ssh_host.sizeHint().width(), + self._lbl_ssh_path.sizeHint().width() + ) + self._lbl_ssh_host.setMinimumWidth(width) + self._lbl_ssh_path.setMinimumWidth(width) + + self._wdg_ssh_proxy = SshProxyWidget( + self, + self.config.sshProxyHost(), + self.config.sshProxyPort(), + self.config.sshProxyUser() + ) + vlayout.addWidget(self._wdg_ssh_proxy) + + # encfs + self._group_mode_local_encfs = self._group_mode_local + self._group_mode_ssh_encfs = self._group_mode_ssh + + # gocryptfs + self._group_mode_local_gocrypt = self._group_mode_local + + # password + group_box = QGroupBox(self) + self._group_password1 = group_box + group_box.setTitle(_('Password')) + tab_layout.addWidget(group_box) + + vlayout = QVBoxLayout(group_box) + + grid = QGridLayout() + + # Used for SSH passphrase & Encfs password + self._lbl_password1 = QLabel(_('Password'), self) + self._txt_password1 = QLineEdit(self) + self._txt_password1.setEchoMode(QLineEdit.EchoMode.Password) + + # Used for Encfs password in "ssh encrypted" mode *rofl* + self._lbl_password2 = QLabel(_('Password'), self) + self._txt_password2 = QLineEdit(self) + self._txt_password2.setEchoMode(QLineEdit.EchoMode.Password) + + # DEBUG + if logger.DEBUG or version.IS_UNSTABLE_DEV_VERSION: + self._lbl_password1.setToolTip('DEBUG - password 1') + self._txt_password1.setToolTip('DEBUG - password 1') + self._lbl_password2.setToolTip('DEBUG - password 2') + self._txt_password2.setToolTip('DEBUG - password 2') + + grid.addWidget(self._lbl_password1, 0, 0) + grid.addWidget(self._txt_password1, 0, 1) + grid.addWidget(self._lbl_password2, 1, 0) + grid.addWidget(self._txt_password2, 1, 1) + vlayout.addLayout(grid) + + self._cb_password_save = QCheckBox(_('Save Password to Keyring'), self) + vlayout.addWidget(self._cb_password_save) + + self._cb_password_use_cache = QCheckBox( + _('Cache Password for Cron (Security ' + 'issue: root can read password)'), + self + ) + vlayout.addWidget(self._cb_password_use_cache) + + self._keyring_supported = tools.KEYRING_SUPPORTED + self._cb_password_save.setEnabled(self._keyring_supported) + + # mode change + self._combo_modes.currentIndexChanged.connect( + self._parent_dialog.slot_combo_modes_changed) + + # host, user, profile id + group_box = QGroupBox(self) + self._frame_advanced = group_box + group_box.setTitle(_('Advanced')) + tab_layout.addWidget(group_box) + + hlayout = QHBoxLayout(group_box) + hlayout.addSpacing(12) + + vlayout2 = QVBoxLayout() + hlayout.addLayout(vlayout2) + + hlayout2 = QHBoxLayout() + vlayout2.addLayout(hlayout2) + + self._lbl_host = QLabel(_('Host:'), self) + hlayout2.addWidget(self._lbl_host) + self._txt_host = QLineEdit(self) + self._txt_host.textChanged.connect(self._slot_full_path_changed) + hlayout2.addWidget(self._txt_host) + + self._lbl_user = QLabel(_('User:'), self) + hlayout2.addWidget(self._lbl_user) + self._txt_user = QLineEdit(self) + self._txt_user.textChanged.connect(self._slot_full_path_changed) + hlayout2.addWidget(self._txt_user) + + self._lbl_profile = QLabel(_('Profile:'), self) + hlayout2.addWidget(self._lbl_profile) + self.txt_profile = QLineEdit(self) + self.txt_profile.textChanged.connect(self._slot_full_path_changed) + hlayout2.addWidget(self.txt_profile) + + self._lbl_full_path = QLabel(_('Full backup path:'), self) + self._lbl_full_path.setWordWrap(True) + vlayout2.addWidget(self._lbl_full_path) + + self._wdg_schedule = schedulewidget.ScheduleWidget(self) + + if schedule.CRONTAB_COMMAND is None: + lbl_warning = qttools.create_info_label( + text=_('Scheduling is disabled because no cron installation ' + 'was found. Please install cron to enable scheduled ' + 'backups.') + ) + tab_layout.addWidget(lbl_warning) + + self._wdg_schedule.setHidden(True) + + tab_layout.addWidget(self._wdg_schedule) + + tab_layout.addStretch() + + @property + def mode(self) -> str: + """The backup mode""" + return self._parent_dialog.mode + + @mode.setter + def mode(self, value: str) -> None: + self._parent_dialog.mode = value + + @property + def config(self) -> Config: + """The config instance""" + return self._parent_dialog.config + + @property + def icon(self): + """Workaround. Remove until import of icon module is solved.""" + return self._parent_dialog.icon + + def _load_passwords(self): + """A workaround to fix #2093 until the widgets are refactored and + redesigned. + """ + # password + password_1 = self.config.password( + mode=self.mode, pw_id=1, only_from_keyring=True) + password_2 = self.config.password( + mode=self.mode, pw_id=2, only_from_keyring=True) + + if password_1 is None: + password_1 = '' + + if password_2 is None: + password_2 = '' + + self._txt_password1.setText(password_1) + self._txt_password2.setText(password_2) + + self._cb_password_save.setChecked( + self._keyring_supported + and self.config.passwordSave(mode=self.mode) + ) + + self._cb_password_use_cache.setChecked( + self.config.passwordUseCache(mode=self.mode)) + + def load_values(self) -> Any: + """Set the values of the widgets regarding the current config.""" + backup_mode = self.config.snapshotsMode() + self._combo_modes.select_by_data(backup_mode) + + # If the profile us an deprecated backup mode (#1734) + if 'encfs' in backup_mode: + self._combo_modes.unhide_by_data(backup_mode) + + # local + self._edit_backup_path.setText( + self.config.snapshotsPath(mode='local')) + + # SSH + self._txt_ssh_host.setText(self.config.sshHost()) + self._txt_ssh_port.setText(str(self.config.sshPort())) + self._txt_ssh_user.setText(self.config.sshUser()) + self._txt_ssh_path.setText(self.config.sshSnapshotsPath()) + + # SSH: Priate key file + val = self.config.sshPrivateKeyFile() + + if val is False: + # using key is disabled + val = None + + elif val is None: + # Select key by default if present + try: + val = sshtools.get_private_ssh_key_files()[0] + except IndexError: + # no key available + pass + + self.key_selector.set_key(Path(val) if val else val) + + # local_encfs + if self.mode == 'local_encfs': + self._edit_backup_path.setText(self.config.localEncfsPath()) + + # local_gocryptfs + if self.mode == 'local_gocryptfs': + self._edit_backup_path.setText(self.config.localGocryptfsPath()) + + self._load_passwords() + + host, user, profile = self.config.hostUserProfile() + self._txt_host.setText(host) + self._txt_user.setText(user) + self.txt_profile.setText(profile) + + # Schedule + self._wdg_schedule.load_values(self.config) + + def _store_local_gocryptfs_destination_path(self) -> bool: + """Path and password related to local gocryptfs profile. + """ + + # save local_gocryptfs + if self.get_active_snapshots_mode() != 'local_gocryptfs': + return True + + # backup path + path = self._edit_backup_path.text() + + if path and Path(path).exists(): + self.config.setLocalGocryptfsPath(path) + + else: + messagebox.warning( + _('The backup destination path cannot be empty.'), + _('Where to save backups'), + self + ) + return False + + # password + password_1 = self._txt_password1.text() + + if not password_1: + messagebox.warning( + _('The encryption password cannot be empty.'), + _('Encryption'), + self + ) + return False + + return True + + def store_values(self) -> bool: + """Store the tab's values into the config instance. + + Returns: + bool: Success or not. + """ + mode = self.get_active_snapshots_mode() + self.config.setSnapshotsMode(mode) + + # WTF!!! + # passwords + password_1 = self._txt_password1.text() + password_2 = self._txt_password2.text() + + mount_kwargs = {} + + if mode in ('ssh', 'local_encfs'): + mount_kwargs = {'password': password_1} + + elif mode == 'ssh_encfs': + mount_kwargs = {'ssh_password': password_1, + 'encfs_password': password_2} + + self.config.setHostUserProfile( + self._txt_host.text(), + self._txt_user.text(), + self.txt_profile.text() + ) + + # SSH + self.config.setSshHost(self._txt_ssh_host.text()) + self.config.setSshPort(self._txt_ssh_port.text()) + self.config.setSshUser(self._txt_ssh_user.text()) + sshproxy_vals = self._wdg_ssh_proxy.values() + self.config.setSshProxyHost(sshproxy_vals['host']) + self.config.setSshProxyPort(sshproxy_vals['port']) + self.config.setSshProxyUser(sshproxy_vals['user']) + self.config.setSshSnapshotsPath(self._txt_ssh_path.text()) + + # SSH key file + if 'ssh' in mode: + key_file = self.key_selector.get_key() + self.config.setSshPrivateKeyFile(str(key_file) if key_file else '') + + # save local_encfs + self.config.setLocalEncfsPath(self._edit_backup_path.text()) + + # _gocryptfs: path & password + if self._store_local_gocryptfs_destination_path() is False: + return False + + # schedule + success = self._wdg_schedule.store_values(self.config) + + if success is False: + return False + + # save password + self.config.setPasswordSave(self._cb_password_save.isChecked(), + mode=mode) + self.config.setPasswordUseCache( + self._cb_password_use_cache.isChecked(), + mode=mode) + self.config.setPassword(password_1, mode=mode) + self.config.setPassword(password_2, mode=mode, pw_id=2) + + if mode != 'local': + mnt = mount.Mount(cfg=self.config, tmp_mount=True, parent=self) + hash_id = self._do_alot_pre_mount_checking(mnt, mount_kwargs) + + if hash_id is False: + return False + + # snaphots_path + if mode == 'local': + self.config.set_snapshots_path(self._edit_backup_path.text()) + + snapshots_mountpoint = self.config.get_snapshots_mountpoint( + tmp_mount=True) + + success = tools.validate_and_prepare_snapshots_path( + path=snapshots_mountpoint, + host_user_profile=self.config.hostUserProfile(), + mode=mode, + copy_links=self.config.copyLinks(), + error_handler=self.config.notifyError) + + if success is False: + return False + + # umount + if mode != 'local': + try: + mnt.umount(hash_id=hash_id) + + except MountException as ex: + messagebox.critical(self, str(ex)) + return False + + return True + + def _do_alot_pre_mount_checking(self, mnt, mount_kwargs): # noqa: PLR0911 + """Initiate several checks related to mounting and similar tasks. + + Depending on the backup mode used different checks are initiated. + + Dev note (buhtz, 2024-09): The code is parked and ready to refactoring. + + Returns: + bool: ``True`` if successful otherwise ``False``. + """ + # pylint: disable=too-many-return-statements + + try: + mode = self.config.snapshotsMode() + if 'gocryptfs' in mode: + if not mnt.get_backend(mode).isConfigured(): + mnt.init_backend(mode=mode, **mount_kwargs) + + except MountException as ex: + messagebox.critical(self, str(ex)) + + return False + + try: + # This will run several checks depending on the snapshots mode + # used. Exceptions are raised if something goes wrong. On mode + # "local" nothing is checked. + mnt.preMountCheck( + mode=self.config.snapshotsMode(), + first_run=True, + **mount_kwargs) + + except NoPubKeyLogin as ex: + logger.error(str(ex), self) + + if not self.config.sshPrivateKeyFile_enabled(): + # Configured without explicit SSH key file + messagebox.critical(self, str(ex)) + return False + + question = ( + '

' + _('An error occurred while attempting to log in to ' + 'the remote host. The following error message was ' + 'returned:') + + '

' + str(ex) + '

' + + _('To enable password-less login, the public SSH key can be ' + 'copied to the remote host.') + + '

' + + _('Proceed with copying the SSH key?') + + '

' + ) + + answer = messagebox.warning(text=question, as_question=True) + + if not answer: + return False + + rc_copy_id = sshtools.sshCopyId( + self.config.sshPrivateKeyFile() + '.pub', + self.config.sshUser(), + self.config.sshHost(), + port=str(self.config.sshPort()), + proxy_user=self.config.sshProxyUser(), + proxy_host=self.config.sshProxyHost(), + proxy_port=self.config.sshProxyPort(), + # This will open an extra input dialog to ask for the + # SSH password. + askPass=tools.which('backintime-askpass'), + cipher=self.config.sshCipher() + ) + + if not rc_copy_id: + messagebox.warning(_( + 'The public SSH key could not be copied. This may ' + 'be due to a connection or permission issue.' + )) + return False + + # --- DEV NOTE TODO --- + # Why this recursive call? + return self._parent_dialog.save_profile() + + except KnownHost as ex: + logger.error(str(ex), self) + fingerprint, hashed_key, key_type = sshtools.sshHostKey( + host=self.config.sshHost(), + port=str(self.config.sshPort())) + + if not fingerprint: + messagebox.critical(self, str(ex)) + return False + + msg = ( + '

' + + _("The authenticity of host {host} can't be " + "established.").format(host=self.config.sshHost()) + + '

' + + _('{keytype} key fingerprint is:').format(keytype=key_type) + + '

' + + fingerprint + + '

' + + _('Please verify this fingerprint. Add it to the ' + '"known_hosts" file?') + + '

' + ) + + if messagebox.question(msg): + sshtools.writeKnownHostsFile(hashed_key) + + # --- DEV NOTE TODO --- + # AGAIN: Why this recursive call? + return self.saveProfile() + + return False + + except MountException as ex: + messagebox.critical(self, str(ex)) + return False + + # okay, let's try to mount + try: + hash_id = mnt.mount( + mode=self.config.snapshotsMode(), + check=False, + **mount_kwargs) + + except MountException as ex: + messagebox.critical(self, str(ex)) + return False + + return hash_id + + def _snapshot_mode_combobox(self) -> combobox.BitComboBox: + # Workaround until encryption transition (#1734) is finished. + + # # Find out if profiles using EncFS + # all_used_modes = { + # self.config.snapshotsMode(pid) for pid in self.config.profiles() + # } + # print(f'{all_used_modes=}') # DEBUG + + snapshot_modes = {} + for key in self.config.SNAPSHOT_MODES: + snapshot_modes[key] = self.config.SNAPSHOT_MODES[key][1] + + return combobox.BitComboBox(self, snapshot_modes) + + def _create_label_encfs_deprecation(self): + # encfs deprecation warning (see #1734, #1735) + + whitepaper = f'' + whitepaper = whitepaper + 'whitepaper' + '' + + txt = [ + 'Encrypted profiles using EncFS are no longer ' + 'supported.', + 'New EncFS backup profiles can not be created anymore. ' + 'Existing EncFS profiles are still displayed and ' + 'supported for now, but EncFS support will be ' + 'completely removed in a future release ' + '(expected around 2027).', + 'EncFS is considered insecure and is no longer actively ' + 'maintained. For more information, see this ' + f'{whitepaper}.' + ] + txt = '

' + '

'.join(txt) + '

' + + return qttools.create_warning_label(txt, icon_scale_factor=3) + + def _slot_snapshots_path_clicked(self): + old_path = Path(self._edit_backup_path.text()) + + dlg = FileDialog( + parent=self, + title=_('Where to save backups'), + show_hidden=True, + allow_multiselection=False, + dirs_only=True, + start_dir=old_path) + path = dlg.result() + + # nothing selected (Cancel) + if not path: + return + + # nothing changed + if old_path and old_path == path: + return + + # gocryptfs destination need to be empty + if 'gocryptfs' in self.mode: + # is not empty + if not self._is_gocryptfs_path_empty(path): + return + + # Really change? + answer = messagebox.question( + text=_('Really change the backup directory?'), + widget_to_center_on=self) + + if not answer: + return + + # Set the path + self._edit_backup_path.setText(str(path)) + + def _is_gocryptfs_path_empty(self, path: Path) -> bool: + # is not empty + if not any(path.iterdir()): + return True + + messagebox.warning( + '

' + + _('The selected backup destination is not empty.') + + '

' + + _('It must be empty to use encryption.') + + '

', + widget_to_center_on=self + ) + + return False + + def _slot_ssh_private_key_file_clicked(self): + key_file = self.key_selector.get_key() + + if key_file: + start_dir = key_file.parent + else: + start_dir = DIR_SSH_KEYS + + file_dialog = FileDialog( + parent=self, + title=_('SSH private key'), + start_dir=start_dir, + allow_multiselection=False + ) + + key_file = file_dialog.result() + + if not key_file: + return + + # No public key + if key_file.suffix.lower() == '.pub': + title = _('Invalid file: Not a private SSH key') + msg = _('The selected file ({path}) is a public SSH key. ' + 'Please choose the corresponding private key file instead ' + '(without ".pub").').format(path=key_file) + messagebox.warning(msg, title, self) + + return + + # self.txtSshPrivateKeyFile.setText(str(key_file)) + self.key_selector.add_and_select_key(key_file) + + def _slot_ssh_key_gen_clicked(self): + + default_keyfile_name = sshtools.determine_default_ssh_key_filename() + + if not default_keyfile_name: + msg = 'Unable to determine the default filename for new ' \ + 'generated ssh keys used by "ssh-keygen".' + logger.critical(msg) + messagebox.critical(self, msg) + return + + key_file_path = DIR_SSH_KEYS / default_keyfile_name + + if key_file_path.exists(): + msg = _('The file {path} already exists. Cannot create a new ' + 'SSH key with that name.').format(path=key_file_path) + messagebox.critical(self, msg) + return + + # Generate the key + if sshtools.sshKeyGen(str(key_file_path)): + self.key_selector.add_and_select_key(key_file_path) + return + + msg = _('Failed to create new SSH key in {path}.') \ + .format(path=key_file_path) + messagebox.critical(self, msg) + + def _slot_full_path_changed(self, _text: Any): + if self.mode in ('ssh', 'ssh_encfs'): + path = self._txt_ssh_path.text() + + else: + path = self._edit_backup_path.text() + + self._lbl_full_path.setText( + _('Full backup path:') + ' ' + + os.path.join( + path, + 'backintime', + self._txt_host.text(), + self._txt_user.text(), + self.txt_profile.text() + )) + + def get_active_snapshots_mode(self) -> str: + """Current profile mode""" + return self._combo_modes.current_data + + def handle_combo_modes_changed(self): + """Hide/show widget elements related to one of + the four snapshot modes. + + This is not a slot connected to a signal. But it is called by the + parent dialog. + """ + # Mode selected in the combo box + active_mode = self.get_active_snapshots_mode() + + # state_data = StateData() + # profile_state = state_data.profile(self.config.currentProfile()) + + # New selected mode different from previous one? + if active_mode != self.mode: + + self.mode = active_mode + + self._group_mode_local.setVisible( + active_mode in ('local', 'local_encfs', 'local_gocryptfs')) + + self._group_mode_ssh.setVisible( + 'ssh' in active_mode) + + self._wdg_schedule.allow_udev( + active_mode in ('local', 'local_encfs', 'local_gocryptfs')) + + # gocryptfs destination need to be empty + if 'gocryptfs' in self.mode: + path = self._edit_backup_path.text() + # dir exists and is not empty + if path and any(Path(path).iterdir()): + self._edit_backup_path.setText('') + + # Don't offer deprecated modes (#1734) + modes_to_hide = {'local_encfs', 'ssh_encfs'} - {active_mode} + for hide in modes_to_hide: + self._combo_modes.hide_by_data(hide) + + # A mode using password fields? + if self.config.modeNeedPassword(active_mode): + + self._lbl_password1.setText( + self.config.SNAPSHOT_MODES[active_mode][2] + ':') + + self._group_password1.show() + + if self.config.modeNeedPassword(active_mode, 2): + self._lbl_password2.setText( + self.config.SNAPSHOT_MODES[active_mode][3] + ':') + self._lbl_password2.show() + self._txt_password2.show() + + else: + self._lbl_password2.hide() + self._txt_password2.hide() + + self._load_passwords() + + else: + self._group_password1.hide() + + # EncFS deprecation warnings (see #1734) + if active_mode in ('local_encfs', 'ssh_encfs'): + self._lbl_encfs_warning.show() + + # # Workaround to avoid showing the warning messagebox just when + # # opening the manage profiles dialog. + # if self._parent_dialog.isVisible(): + # # Show the profile specific warning dialog only once per + # # profile. + # if profile_state.msg_encfs < ENCFS_MSG_STAGE: + # profile_state.msg_encfs = ENCFS_MSG_STAGE + # dlg = encfsmsgbox.EncfsCreateWarning(self) + # dlg.exec() + + else: + self._lbl_encfs_warning.hide() diff --git a/qt/manageprofiles/tab_include.py b/qt/manageprofiles/tab_include.py new file mode 100644 index 000000000..f4216fd29 --- /dev/null +++ b/qt/manageprofiles/tab_include.py @@ -0,0 +1,219 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# SPDX-FileCopyrightText: © 2025 Devin Black +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""The IncludeTab class for managing include paths""" +from pathlib import Path +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import (QWidget, + QVBoxLayout, + QHBoxLayout, + QTreeWidget, + QTreeWidgetItem, + QPushButton, + QHeaderView, + QAbstractItemView) +from qttools import custom_sort_order +from filedialog import FileDialog + + +class IncludeTab(QWidget): + """Tab for managing include files and directories.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent): + super().__init__(parent=parent) + + self._parent_dialog = parent + self.icon = parent.icon + self.config = parent.config + + layout = QVBoxLayout(self) + + self.list_include = QTreeWidget(self) + self.list_include.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection + ) + self.list_include.setRootIsDecorated(False) + self.list_include.setHeaderLabels([ + _('Include files and directories'), 'Count' + ]) + self.list_include.header().setSectionResizeMode( + 0, QHeaderView.ResizeMode.Stretch + ) + self.list_include.header().setSectionsClickable(True) + self.list_include.header().setSortIndicatorShown(True) + self.list_include.header().setSectionHidden(1, True) + layout.addWidget(self.list_include) + + self.list_include_count = 0 + self.list_include_sort_loop = False + self.list_include.header().sortIndicatorChanged.connect( + self.include_custom_sort_order + ) + + buttons_layout = QHBoxLayout() + layout.addLayout(buttons_layout) + + self.btn_include_file = QPushButton( + self.icon.ADD, _('Add files'), self) + buttons_layout.addWidget(self.btn_include_file) + self.btn_include_file.clicked.connect(self.btn_include_file_clicked) + + self.btn_include_add = QPushButton( + self.icon.ADD, _('Add directories'), self + ) + buttons_layout.addWidget(self.btn_include_add) + self.btn_include_add.clicked.connect(self.btn_include_add_clicked) + + self.btn_include_remove = QPushButton( + self.icon.REMOVE, _('Remove'), self + ) + buttons_layout.addWidget(self.btn_include_remove) + self.btn_include_remove.clicked.connect( + self.btn_include_remove_clicked + ) + + def load_values(self, profile_state): + """Load config values into the GUI""" + + self.list_include.clear() + for include in self.config.include(): + self.add_include(include) + + try: + incl_sort = profile_state.include_sorting + self.list_include.sortItems( + incl_sort[0], Qt.SortOrder(incl_sort[1]) + ) + except KeyError: + pass + + def store_values(self, profile_state): + """Store values from GUI into the config""" + + profile_state.include_sorting = ( + self.list_include.header().sortIndicatorSection(), + self.list_include.header().sortIndicatorOrder().value + ) + + self.list_include.sortItems(1, Qt.SortOrder.AscendingOrder) + + include_list = [] + for index in range(self.list_include.topLevelItemCount()): + item = self.list_include.topLevelItem(index) + include_list.append( + (item.text(0), item.data(0, Qt.ItemDataRole.UserRole)) + ) + + self.config.setInclude(include_list) + + def add_include(self, data): + """Add a file or directory to the list.""" + item = QTreeWidgetItem() + icon = self.icon.FOLDER if data[1] == 0 else self.icon.FILE + item.setIcon(0, icon) + + duplicates = self.list_include.findItems( + data[0], + Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchCaseSensitive + ) + + if duplicates: + self.list_include.setCurrentItem(duplicates[0]) + return + + item.setText(0, data[0]) + item.setData(0, Qt.ItemDataRole.UserRole, data[1]) + + self.list_include_count += 1 + item.setText(1, str(self.list_include_count).zfill(6)) + item.setData(1, Qt.ItemDataRole.UserRole, self.list_include_count) + self.list_include.addTopLevelItem(item) + self.list_include.setCurrentItem(item) + + def btn_include_remove_clicked(self): + """Handle removal of selected include entries.""" + for item in self.list_include.selectedItems(): + index = self.list_include.indexOfTopLevelItem(item) + if index >= 0: + self.list_include.takeTopLevelItem(index) + if self.list_include.topLevelItemCount() > 0: + self.list_include.setCurrentItem(self.list_include.topLevelItem(0)) + + def _copy_links_or_unsafe_links(self) -> bool: + """Return `True` if one of the two Expert Options "Copy links" and + "Copy unsafe links" are set/checked. + + Dev note (buhtz, 2025-10): The values from config are used. Keep in + mind that these do not have to reflect the check boxes in the Expert + Options TAB. Modifications in those checkboxes are not stored to config + immediatel but only after clicking OK to the Manage Profiles dialog. + This behavior of the dialog need to be changed. + + """ + return self.config.copyUnsafeLinks() or self.config.copyLinks() + + def _ask_include_symlinks_target(self, path: Path): + question_msg = _( + '"{path}" is a symlink. The linked target will not be backed up ' + 'until it is included, too.').format(path=path) + + question_msg = question_msg + '\n' + _( + "Include the symlink's target instead?") + + return self._parent_dialog.questionHandler(question_msg) + + def btn_include_file_clicked(self): + """Handle file-adding button click.""" + dlg = FileDialog( + parent=self, + title=_('Include files'), + show_hidden=True, + allow_multiselection=True, + dirs_only=False) + + for path in dlg.result(): + if not path: + continue + + if path.is_symlink() and not self._copy_links_or_unsafe_links(): + if self._ask_include_symlinks_target(path): + path = path.resolve() + + self.add_include((str(path), 1)) + + def btn_include_add_clicked(self): + """Handle directory-adding button click.""" + # pylint: disable=duplicate-code + dlg = FileDialog(parent=self, + title=_('Include directories'), + show_hidden=True, + allow_multiselection=True, + dirs_only=True) + + for path in dlg.result(): + + if not path: + continue + + if path.is_symlink() and not self._copy_links_or_unsafe_links(): + if self._ask_include_symlinks_target(path): + path = path.resolve() + + self.add_include((str(path), 0)) + + def include_custom_sort_order(self, *args): + """Trigger custom sort order when header is clicked.""" + self.list_include_sort_loop = custom_sort_order( + self.list_include.header(), self.list_include_sort_loop, *args + ) diff --git a/qt/manageprofiles/tab_options.py b/qt/manageprofiles/tab_options.py new file mode 100644 index 000000000..8688e69bf --- /dev/null +++ b/qt/manageprofiles/tab_options.py @@ -0,0 +1,190 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module about the Options tab""" +from PyQt6.QtWidgets import (QDialog, + QVBoxLayout, + QHBoxLayout, + QLabel, + QCheckBox) +from config import Config +import tools +from event import Event +import qttools +from manageprofiles import combobox +from manageprofiles.statebindcheckbox import StateBindCheckBox +from manageprofiles.storagesizewidget import StorageSizeWidget + + +class OptionsTab(QDialog): + """The 'Options' tab in the Manage Profiles dialog.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent): + super().__init__(parent=parent) + + self._parent_dialog = parent + + tab_layout = QVBoxLayout(self) + + self._cb_notify = QCheckBox(_('Enable notifications'), self) + tab_layout.addWidget(self._cb_notify) + + self._cb_no_backup_on_battery \ + = QCheckBox(_('Disable backups when on battery'), self) + tab_layout.addWidget(self._cb_no_backup_on_battery) + + if not tools.powerStatusAvailable(): + self._cb_no_backup_on_battery.setEnabled(False) + self._cb_no_backup_on_battery.setToolTip( + _('Power status not available from system')) + + self._cb_one_backup_at_a_time \ + = QCheckBox(_('Run only one backup at a time')) + tab_layout.addWidget(self._cb_one_backup_at_a_time) + qttools.set_wrapped_tooltip( + self._cb_one_backup_at_a_time, + _('Other backups will be blocked until the current backup is ' + 'completed. This is a global setting, meaning it will affect ' + 'all profiles for this user. However, it must also be ' + 'activated for all other users.') + ) + + self._cb_backup_on_restore = QCheckBox( + _('Backup replaced files on restore'), self) + tab_layout.addWidget(self._cb_backup_on_restore) + backup_suffix = self._parent_dialog.snapshots.backupSuffix() + qttools.set_wrapped_tooltip( + self._cb_backup_on_restore, + [ + _("Before restoring, newer versions of files will be renamed " + "with the appended {suffix}. These files can be removed " + "with the following command:").format( + suffix=backup_suffix), + f'find ./ -name "*{backup_suffix}" -delete' + ] + ) + + self._cb_continue_on_errors = QCheckBox( + _('Continue on errors (keep incomplete backups)'), self) + tab_layout.addWidget(self._cb_continue_on_errors) + + self._cb_use_checksum = QCheckBox( + _('Use checksum to detect changes'), self) + tab_layout.addWidget(self._cb_use_checksum) + + self._cb_backup_regard_changes = QCheckBox( + _('Create a new backup whether there were changes or not.')) + tab_layout.addWidget(self._cb_backup_regard_changes) + + # warn free space + hlayout = QHBoxLayout() + tab_layout.addLayout(hlayout) + + self._su_warn_free_space = StorageSizeWidget(self, (1, 9999999)) + self._cb_warn_free_space = StateBindCheckBox( + _('Warn if the free disk space falls below'), self) + self._cb_warn_free_space.bind(self._su_warn_free_space) + hlayout.addWidget(self._cb_warn_free_space) + hlayout.addWidget(self._su_warn_free_space) + + tooltip = [ + _('Shows a warning when free space on the backup destination disk ' + 'is less than the specified value.'), + _('If the Remove & Retention policy is enabled and old backups ' + 'are removed based on available free space, this value cannot ' + 'be lower than the value set in the policy.') + ] + qttools.set_wrapped_tooltip(self._su_warn_free_space, tooltip) + qttools.set_wrapped_tooltip(self._cb_warn_free_space, tooltip) + + # Event: Notify observers if "remove less free space" value has changed + self.event_warn_free_space_value_changed = Event() + # pylint: disable=unnecessary-lambda + self._su_warn_free_space.event_value_changed.register( + lambda value: # noqa: PLW0108 + self.event_warn_free_space_value_changed.notify(value) + ) + + # log level + hlayout = QHBoxLayout() + tab_layout.addLayout(hlayout) + + hlayout.addWidget(QLabel(_('Log Level:'), self)) + + self._combo_log_level = self._create_combo_log_level() + hlayout.addWidget(self._combo_log_level) + hlayout.addStretch() + + tab_layout.addStretch() + + @property + def config(self) -> Config: + """The config instance.""" + return self._parent_dialog.config + + def load_values(self): + """Load config values into the GUI""" + self._cb_notify.setChecked(self.config.notify()) + self._cb_no_backup_on_battery.setChecked( + self.config.noSnapshotOnBattery()) + self._cb_one_backup_at_a_time.setChecked(self.config.globalFlock()) + self._cb_backup_on_restore.setChecked(self.config.backupOnRestore()) + self._cb_continue_on_errors.setChecked(self.config.continueOnErrors()) + self._cb_use_checksum.setChecked(self.config.useChecksum()) + self._cb_backup_regard_changes.setChecked( + self.config.takeSnapshotRegardlessOfChanges()) + value = self.config.warnFreeSpace() + self._cb_warn_free_space.setChecked(self.config.warnFreeSpaceEnabled()) + self._su_warn_free_space.set_storagesize(value) + self._combo_log_level.select_by_data(self.config.logLevel()) + + def store_values(self): + """Store values from GUI into the config""" + self.config.setNotify(self._cb_notify.isChecked()) + self.config.setNoSnapshotOnBattery( + self._cb_no_backup_on_battery.isChecked()) + self.config.setGlobalFlock(self._cb_one_backup_at_a_time.isChecked()) + self.config.setBackupOnRestore(self._cb_backup_on_restore.isChecked()) + self.config.setContinueOnErrors( + self._cb_continue_on_errors.isChecked()) + self.config.setUseChecksum(self._cb_use_checksum.isChecked()) + self.config.setTakeSnapshotRegardlessOfChanges( + self._cb_backup_regard_changes.isChecked()) + if self._su_warn_free_space.isEnabled(): + self.config.setWarnFreeSpace( + self._su_warn_free_space.get_storagesize()) + else: + self.config.setWarnFreeSpaceDisabled() + + self.config.setLogLevel(self._combo_log_level.current_data) + + def remove_free_space_value_changed(self, value): + """Event handler in case the value of 'Remove if less than X free + space' in 'Remove & Retention' tab was modified. + + That value can not be lower than 'Warn on free space' value. + """ + warn_val = self._su_warn_free_space.get_storagesize() + + if warn_val < value: + self._su_warn_free_space.set_storagesize( + value, dont_touch_unit=True) + + def _create_combo_log_level(self) -> combobox.BitComboBox: + fill = { + 0: _('None'), + 1: _('Errors'), + 2: _('Changes') + ' & ' + _('Errors'), + 3: _('All'), + } + return combobox.BitComboBox(self, fill) diff --git a/qt/manageprofiles/tab_remove_retention.py b/qt/manageprofiles/tab_remove_retention.py new file mode 100644 index 000000000..c215e2581 --- /dev/null +++ b/qt/manageprofiles/tab_remove_retention.py @@ -0,0 +1,422 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module about the Remove & Retention policy tab""" +from PyQt6.QtWidgets import (QCheckBox, + QDialog, + QGridLayout, + QGroupBox, + QHBoxLayout, + QLabel, + QSpinBox, + QToolTip, + QWidget) +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QCursor +from config import Config +from bitbase import TimeUnit +from event import Event +import qttools +from manageprofiles.statebindcheckbox import StateBindCheckBox +from manageprofiles.spinboxunit import SpinBoxWithUnit +from manageprofiles.storagesizewidget import StorageSizeWidget +from bitwidgets import HLineWidget + + +class RemoveRetentionTab(QDialog): + """The 'Remove & Retention' tab in the Manage Profiles dialog.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent): + super().__init__(parent=parent) + + self._parent_dialog = parent + + # Vertical main layout + # self._tab_layout = QVBoxLayout(self) + self._tab_layout = QGridLayout() + self.setLayout(self._tab_layout) + + # Keep most recent + self._label_keep_most_recent() + + # Keep named backups + self._cb_keep_named = self._checkbox_keep_named() + + # --- + self._tab_layout.addWidget( + HLineWidget(), + # fromRow + self._tab_layout.rowCount(), + # fromColumn + 0, + # rowSpan, + 1, + # columnSpan + 3) + + # Icon & Info label + self._label_rule_execute_order() + + # --- + self._tab_layout.addWidget( + HLineWidget(), + # fromRow + self._tab_layout.rowCount(), + # fromColumn + 0, + # rowSpan, + 1, + # columnSpan + 3) + + # Remove older than N years/months/days + self._checkbox_remove_older, self._spinunit_remove_older \ + = self._remove_older_than() + row = self._tab_layout.rowCount() + self._tab_layout.addWidget(self._checkbox_remove_older, row, 0, 1, 2) + self._tab_layout.addWidget(self._spinunit_remove_older, row, 2) + + # Retention policy + self._cb_retention_policy, \ + self._cb_run_remote_in_background, \ + self._spb_keep_all, \ + self._spb_keep_one_per_day, \ + self._spb_keep_one_per_week, \ + self._spb_keep_one_per_month \ + = self._groupbox_retention_policy() + + self._checkbox_space, \ + self._spin_unit_space, \ + self._checkbox_inodes, \ + self._spin_inodes \ + = self._remove_free_space_inodes() + + # Layout + self._tab_layout.setColumnStretch(0, 2) + self._tab_layout.setColumnStretch(1, 1) + self._tab_layout.setColumnStretch(2, 0) + self._tab_layout.setRowStretch(self._tab_layout.rowCount(), 1) + + # Event: Notify observers if "warn free space" value has changed + self.event_remove_free_space_value_changed = Event() + + # pylint: disable=unnecessary-lambda + self._spin_unit_space.event_value_changed.register( + lambda value: # noqa: PLW0108 + self.event_remove_free_space_value_changed.notify(value) + ) + + @property + def config(self) -> Config: + """The config instance""" + return self._parent_dialog.config + + def load_values(self): + """Load config values into the GUI""" + + # don't remove named snapshots + self._cb_keep_named.setChecked( + self.config.dontRemoveNamedSnapshots()) + + # remove old snapshots + enabled, value, unit = self.config.removeOldSnapshots() + self._checkbox_remove_older.setChecked(enabled) + self._spinunit_remove_older.set_value(value) + self._spinunit_remove_older.select_unit(unit) + + # smart remove + smart_remove, keep_all, keep_one_per_day, keep_one_per_week, \ + keep_one_per_month = self.config.smartRemove() + self._cb_retention_policy.setChecked(smart_remove) + self._spb_keep_all.setValue(keep_all) + self._spb_keep_one_per_day.setValue(keep_one_per_day) + self._spb_keep_one_per_week.setValue(keep_one_per_week) + self._spb_keep_one_per_month.setValue(keep_one_per_month) + self._cb_run_remote_in_background.setChecked( + self.config.smartRemoveRunRemoteInBackground()) + + # min free space + enabled, value = self.config.minFreeSpaceAsStorageSize() + self._checkbox_space.setChecked(enabled) + self._spin_unit_space.set_storagesize(value) + + # min free inodes + self._checkbox_inodes.setChecked(self.config.minFreeInodesEnabled()) + self._spin_inodes.setValue(self.config.minFreeInodes()) + + def store_values(self): + """Store values from GUI into the config""" + + self.config.setRemoveOldSnapshots( + self._checkbox_remove_older.isChecked(), + self._spinunit_remove_older.value(), + self._spinunit_remove_older.unit() + ) + + self.config.setDontRemoveNamedSnapshots( + self._cb_keep_named.isChecked()) + + self.config.setSmartRemove( + self._cb_retention_policy.isChecked(), + self._spb_keep_all.value(), + self._spb_keep_one_per_day.value(), + self._spb_keep_one_per_week.value(), + self._spb_keep_one_per_month.value()) + + self.config.setSmartRemoveRunRemoteInBackground( + self._cb_run_remote_in_background.isChecked()) + + self.config.setMinFreeSpaceWithStorageSize( + self._spin_unit_space.isEnabled(), + self._spin_unit_space.get_storagesize()) + + self.config.setMinFreeInodes( + self._spin_inodes.isEnabled(), + self._spin_inodes.value()) + + def warn_free_space_value_changed(self, value): + """See tab_options.py::OptionsTab.remove_free_space_value_changed(). + + The remove value need to be lower than the warn value. + + """ + remove_value = self._spin_unit_space.get_storagesize() + + if remove_value >= value: + self._spin_unit_space.set_storagesize(value, dont_touch_unit=True) + + def update_items_state(self, enabled): + """Update items regarding profile modes changes.""" + self._cb_run_remote_in_background.setVisible(enabled) + + def _label_rule_execute_order(self) -> QWidget: + icon_label = qttools.create_icon_label_info(fixed_size_widget=True) + + # Info text + manual_link = '' + _('user manual') + '' + txt = _( + 'The following rules are processed from top to bottom. Later ' + 'rules override earlier ones. See the {manual_link} for details ' + 'and examples.').format(manual_link=manual_link) + txt_label = QLabel(txt) + txt_label.setWordWrap(True) + + txt_label.linkActivated.connect(self._handle_link_activated) + + txt_label.setTextInteractionFlags( + Qt.TextInteractionFlag.TextBrowserInteraction) + + # Show URL in tooltip without anoing http-protocol prefix. + txt_label.linkHovered.connect( + lambda url: QToolTip.showText( + # QCursor.pos(), url.replace('https://', '')) + QCursor.pos(), _('Open user manual in browser.')) + ) + + wdg = QWidget() + layout = QHBoxLayout(wdg) + layout.addWidget(icon_label) + layout.addWidget(txt_label) + + self._tab_layout.addWidget(wdg, self._tab_layout.rowCount(), 0, 1, 3) + + def _handle_link_activated(self, _link): + qttools.open_user_manual() + + def _label_keep_most_recent(self) -> None: + cb = QCheckBox(_('Keep the most recent backup.'), self) + qttools.set_wrapped_tooltip( + cb, + ( + _('The most up-to-date backup is kept under ' + 'all circumstances.'), + _('That behavior cannot be changed.') + ) + ) + + # Always enabled + cb.setChecked(True) + cb.nextCheckState = lambda: None + + # fromRow, fromColumn spanning rowSpan rows and columnSpan + self._tab_layout.addWidget(cb, self._tab_layout.rowCount(), 0, 1, 2) + + def _checkbox_keep_named(self) -> QCheckBox: + cb = QCheckBox(_('Keep named backups.'), self) + qttools.set_wrapped_tooltip( + cb, + _('Backups that have been given a name, in addition to the ' + 'usual timestamp, will be retained under all circumstances ' + 'and will not be removed.') + ) + + # fromRow, fromColumn spanning rowSpan rows and columnSpan + self._tab_layout.addWidget(cb, self._tab_layout.rowCount(), 0, 1, 2) + + return cb + + def _remove_older_than(self) -> QWidget: + # units + units = { + TimeUnit.DAY: _('Day(s)'), + TimeUnit.WEEK: _('Week(s)'), + TimeUnit.YEAR: _('Year(s)') + } + spin_unit = SpinBoxWithUnit(self, (1, 999), units) + + # checkbox + checkbox = StateBindCheckBox(_('Remove backups older than'), self) + checkbox.bind(spin_unit) + + # tooltip + tip = ( + f'{units[TimeUnit.DAY]}: ' + + _('Full days. Current day is ignored.'), + f'{units[TimeUnit.WEEK]}: ' + + _('Calendar weeks with Monday as first day. ' + 'Current week is ignored.'), + f'{units[TimeUnit.YEAR]}: ' + + _('12 months periods. Current month is ignored.') + ) + + qttools.set_wrapped_tooltip(checkbox, tip) + qttools.set_wrapped_tooltip(spin_unit, tip) + + return checkbox, spin_unit + + def _groupbox_retention_policy(self) -> tuple: # noqa: PLR0915 + # pylint: disable=too-many-statements + layout = QGridLayout() + # col, fx + layout.setColumnStretch(0, 1) + layout.setColumnStretch(1, 0) + layout.setColumnStretch(2, 0) + + checkbox_group = QGroupBox(_('Retention policy'), self) + checkbox_group.setCheckable(True) + checkbox_group.setLayout(layout) + + cb_in_background = QCheckBox( + _('Run in background on remote host.'), self) + qttools.set_wrapped_tooltip( + cb_in_background, + (_('The retention policy will be executed directly on the remote ' + 'machine, not locally. The commands "bash", "screen", and ' + '"flock" must be installed and available on that ' + 'remote machine.'), + _('If selected, Back In Time will first test the ' + 'remote machine.'))) + layout.addWidget(cb_in_background, 0, 0, 1, 2) + + tip = _('The days are counted starting from today.') + label = QLabel(_('Keep all backups for the last'), self) + qttools.set_wrapped_tooltip(label, tip) + layout.addWidget(label, 1, 0) + all_last_days = QSpinBox(self) + all_last_days.setRange(1, 999) + all_last_days.setSuffix(' ' + _('day(s).')) + qttools.set_wrapped_tooltip(all_last_days, tip) + # all_last_days.setAlignment(Qt.AlignmentFlag.AlignRight) + layout.addWidget(all_last_days, 1, 1) + + # tip = same as the previous label + label = QLabel( + _('Keep the last backup for each day for the last'), self) + qttools.set_wrapped_tooltip(label, tip) + layout.addWidget(label, 2, 0) + one_per_day = QSpinBox(self) + one_per_day.setRange(1, 999) + one_per_day.setSuffix(' ' + _('day(s).')) + qttools.set_wrapped_tooltip(one_per_day, tip) + # one_per_day.setAlignment(Qt.AlignmentFlag.AlignRight) + layout.addWidget(one_per_day, 2, 1) + + tip = _('The weeks are counted starting from the current running ' + 'week. A week starts on Monday.') + label = QLabel( + _('Keep the last backup for each week for the last'), self) + qttools.set_wrapped_tooltip(label, tip) + layout.addWidget(label, 3, 0) + one_per_week = QSpinBox(self) + one_per_week.setRange(1, 999) + one_per_week.setSuffix(' ' + _('week(s).')) + qttools.set_wrapped_tooltip(one_per_week, tip) + # one_per_week.setAlignment(Qt.AlignmentFlag.AlignRight) + layout.addWidget(one_per_week, 3, 1) + + tip = _('The months are counted as calendar months starting with ' + 'the current month.') + label = QLabel( + _('Keep the last backup for each month for the last'), self) + qttools.set_wrapped_tooltip(label, tip) + layout.addWidget(label, 4, 0) + one_per_month = QSpinBox(self) + one_per_month.setRange(1, 999) + one_per_month.setSuffix(' ' + _('month(s).')) + qttools.set_wrapped_tooltip(one_per_month, tip) + # one_per_month.setAlignment(Qt.AlignmentFlag.AlignRight) + layout.addWidget(one_per_month, 4, 1) + + tip = _('The years are counted as calendar years starting with ' + 'the current year.') + label = QLabel(_('Keep the last backup for each year for'), self) + layout.addWidget(label, 5, 0) + labeltwo = QLabel(_('all years.'), self) + layout.addWidget(labeltwo, 5, 1) + qttools.set_wrapped_tooltip([label, labeltwo], tip) + + self._tab_layout.addWidget( + checkbox_group, self._tab_layout.rowCount(), 0, 1, 3) + + return (checkbox_group, cb_in_background, all_last_days, one_per_day, + one_per_week, one_per_month) + + def _remove_free_space_inodes(self) -> tuple: + # free space less than + spin_unit_space = StorageSizeWidget(self, (1, 99999)) + + checkbox_space = StateBindCheckBox( + _('… the free space is less than'), self) + checkbox_space.bind(spin_unit_space) + + # min free inodes + checkbox_inodes = StateBindCheckBox( + _('… the free inodes are less than'), self) + + spin_inodes = QSpinBox(self) + spin_inodes.setSuffix(' %') + spin_inodes.setRange(0, 15) + + checkbox_inodes.bind(spin_inodes) + + # layout + groupbox = QGroupBox(_('Remove oldest backup if …'), self) + grid = QGridLayout() + groupbox.setLayout(grid) + grid.setColumnStretch(0, 1) + grid.setColumnStretch(1, 0) + grid.setColumnStretch(2, 0) + + # wdg, row, col + grid.addWidget(checkbox_space, 0, 0, 1, 2) + grid.addWidget(spin_unit_space, 0, 2) + grid.addWidget(checkbox_inodes, 1, 0, 1, 2) + grid.addWidget(spin_inodes, 1, 2) + + self._tab_layout.addWidget( + groupbox, + self._tab_layout.rowCount(), + 0, 1, 3 + ) + + return checkbox_space, spin_unit_space, checkbox_inodes, spin_inodes diff --git a/qt/messagebox.py b/qt/messagebox.py index 8a996e8eb..530987b4e 100644 --- a/qt/messagebox.py +++ b/qt/messagebox.py @@ -1,38 +1,43 @@ -# Copyright (C) 2012-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2012-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import gettext -from PyQt5.QtCore import QTimer, Qt -from PyQt5.QtWidgets import QApplication, QMessageBox, QInputDialog, QLineEdit,\ - QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QScrollArea -import qttools - -_ = gettext.gettext +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module offering several message boxes to inform user in the GUI.""" +from PyQt6.QtCore import QTimer +from PyQt6.QtWidgets import (QApplication, + QInputDialog, + QLineEdit, + QMessageBox, + QWidget) +import qttools # pylint: disable=cyclic-import + + +def ask_password_dialog(parent, title, prompt, language_code, timeout): + """Dev note (2025-07, buhtz): + Replace with extern use of zenity, yat, kdialog + e.g. + zenity --entry --title="foo" --text="text" --hide-text + yad --entry --title="foo" --text="text" --hide-text + kdialog --password "enter password" + """ -def askPasswordDialog(parent, title, prompt, timeout = None): if parent is None: - app = qttools.createQApplication() - translator = qttools.translator() + app = qttools.create_qapplication() + translator = qttools.initiate_translator(language_code) app.installTranslator(translator) - import icon + # pylint: disable-next=import-outside-toplevel + import icon # noqa: PLC0415 + dialog = QInputDialog() timer = QTimer() - if not timeout is None: + + if timeout is not None: timer.timeout.connect(dialog.reject) timer.setInterval(timeout * 1000) timer.start() @@ -40,64 +45,95 @@ def askPasswordDialog(parent, title, prompt, timeout = None): dialog.setWindowIcon(icon.BIT_LOGO) dialog.setWindowTitle(title) dialog.setLabelText(prompt) - dialog.setTextEchoMode(QLineEdit.Password) + dialog.setTextEchoMode(QLineEdit.EchoMode.Password) QApplication.processEvents() - ret = dialog.exec_() + result = dialog.exec() timer.stop() - if ret: - password = dialog.textValue() - else: - password = '' - del(dialog) - return(password) + password = dialog.textValue() if result else '' + + del dialog + + return password + + +def info(text, title=None, widget_to_center_on=None): + """Show a modal information message box. + + The message box is centered on the primary screen if + ``widget_to_center_on`` is not given. + + Args: + text(str): The information text central to the dialog. + title(str): Title of the message box dialog. + widget_to_center_on(QWidget): Center the message box on that widget. + """ + QMessageBox.information( + widget_to_center_on, + title if title else ngettext('Information', 'Information', 1), + text) + + +def warning(text: str, + title: str = None, + widget_to_center_on: QWidget = None, + as_question: bool = False) -> bool | None: + """Show a modal warning message box. + + The message box is centered on the primary screen if + ``widget_to_center_on`` is not given. + + Args: + text: The warning message central to the dialog. + title: Title of the message box dialog (default: 'Warning'). + widget_to_center_on: Center the message box on that widget. + as_question: Use Yes and No buttons. + """ + buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No \ + if as_question else QMessageBox.StandardButton.Ok + + answer = QMessageBox.warning( + widget_to_center_on, + title if title else _('Warning'), + text, + buttons + ) + + return answer == QMessageBox.StandardButton.Yes + + +def question(text, title=None, widget_to_center_on=None) -> bool: + """Show a modal question message box. + + The message box is centered on the primary screen if + ``widget_to_center_on`` is not given. + + Args: + text(str): The question central to the dialog. + title(str): Title of the message box dialog (default: 'Question'). + widget_to_center_on(QWidget): Center the message box on that widget. + + Return: + bool: ``True`` if the answer was "Yes", otherwise ``False``. + """ + answer = QMessageBox.question( + widget_to_center_on, + title if title else _('Question'), + text) + + return answer == QMessageBox.StandardButton.Yes + def critical(parent, msg): - return QMessageBox.critical(parent, _('Error'), - msg, - buttons = QMessageBox.Ok, - defaultButton = QMessageBox.Ok) - -def warningYesNo(parent, msg): - return QMessageBox.question(parent, _('Question'), msg, - buttons = QMessageBox.Yes | QMessageBox.No, - defaultButton = QMessageBox.No) - -def warningYesNoOptions(parent, msg, options = ()): - dlg = QDialog(parent) - dlg.setWindowTitle(_('Question')) - layout = QVBoxLayout() - dlg.setLayout(layout) - label = QLabel(msg) - layout.addWidget(label) - - for opt in options: - layout.addWidget(opt['widget']) - - buttonBox = QDialogButtonBox(QDialogButtonBox.Yes | QDialogButtonBox.No) - buttonBox.button(QDialogButtonBox.No).setDefault(True) - layout.addWidget(buttonBox) - buttonBox.accepted.connect(dlg.accept) - buttonBox.rejected.connect(dlg.reject) - ret = dlg.exec_() - return (ret, {opt['id']:opt['retFunc']() for opt in options if opt['retFunc'] is not None}) - -def showInfo(parent, title, msg): - dlg = QDialog(parent) - dlg.setWindowTitle(title) - vlayout = QVBoxLayout(dlg) - label = QLabel(msg) - label.setTextInteractionFlags(Qt.LinksAccessibleByMouse) - label.setOpenExternalLinks(True) - - scroll_area = QScrollArea() - scroll_area.setWidget(label) - - buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) - buttonBox.accepted.connect(dlg.accept) - - vlayout.addWidget(scroll_area) - vlayout.addWidget(buttonBox) - return dlg.exec_() + """Shows an error message box. + + Qt itseld does not distinguish between error and critical messages. + """ + return QMessageBox.critical( + parent, + _('Error'), + msg, + buttons=QMessageBox.StandardButton.Ok, + defaultButton=QMessageBox.StandardButton.Ok) diff --git a/qt/net.launchpad.backintime.policy b/qt/net.launchpad.backintime.policy index d55454f1e..d49db0d87 100644 --- a/qt/net.launchpad.backintime.policy +++ b/qt/net.launchpad.backintime.policy @@ -1,4 +1,13 @@ + @@ -10,7 +19,7 @@ Authentication is required to run Back In Time as root. - Start Back In Time GUI as root. + Start Back In Time GUI as root. auth_admin auth_admin @@ -21,8 +30,8 @@ - Authentication is required to add Udev rules. - This will install Udev rules which will start Back In Time if a drive get connected. + Authentication is required to change backup schedules triggered by external storage device connections. + This will install Udev rules which will start Back In Time if a drive get connected. auth_admin auth_admin_keep @@ -31,8 +40,8 @@ - Authentication is required to delete Udev rules. - This will delete Udev rules. + Authentication is required to change backup schedules triggered by external storage device connections. + This will delete Udev rules related to a specific Back In Time backup profile. auth_admin auth_admin_keep diff --git a/qt/net.launchpad.backintime.serviceHelper.conf b/qt/net.launchpad.backintime.serviceHelper.conf index 4f6913f7b..b54030c4e 100644 --- a/qt/net.launchpad.backintime.serviceHelper.conf +++ b/qt/net.launchpad.backintime.serviceHelper.conf @@ -1,4 +1,14 @@ + diff --git a/qt/net.launchpad.backintime.serviceHelper.service b/qt/net.launchpad.backintime.serviceHelper.service index aceb0c252..ee61e626c 100644 --- a/qt/net.launchpad.backintime.serviceHelper.service +++ b/qt/net.launchpad.backintime.serviceHelper.service @@ -1,3 +1,11 @@ +# SPDX-FileCopyrightText: © 2016 Germar Reitze +# SPDX-FileCopyrightText: © 2017 Matthias Gerstner +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# [D-BUS Service] Name=net.launchpad.backintime.serviceHelper Exec=/usr/bin/python3 -Es /usr/share/backintime/qt/serviceHelper.py diff --git a/qt/placeswidget.py b/qt/placeswidget.py new file mode 100644 index 000000000..2d03f3e38 --- /dev/null +++ b/qt/placeswidget.py @@ -0,0 +1,214 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz +# SPDX-FileCopyrightText: © 2025 Samuel Moore +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# Split from app.py +"""Module offering the Places widget in the main window. +""" +import os +import pathlib +from typing import Optional +from PyQt6.QtWidgets import (QAbstractItemView, + QTreeWidget, + QTreeWidgetItem, + QWidget) +from PyQt6.QtGui import QFont, QIcon, QPalette +from PyQt6.QtCore import Qt +import bitbase +import config +from profile_operations import ProfileOperations + + +class PlacesWidget(QTreeWidget): + """A tree widget used in the main window. + + It contain the file system root and current users home directory as entry + points. It also contain all included backup directories as entries. + """ + + def __init__(self, parent: QWidget, cfg: config.Config): + QTreeWidget.__init__(self, parent=parent) + + self.config = cfg + self.parent = parent + + self._profile_operations = None + + # Do not show controls for expanding and collapsing top-level items + self.setRootIsDecorated(False) + + self.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + + self.setHeaderLabel(_('Shortcuts')) + + self.header().setSectionsClickable(True) + self.header().setSortIndicatorShown(True) + self.header().setSectionHidden(1, True) + + self.header().sortIndicatorChanged.connect( + self._on_sort_indicator_changed) + + # previous and new item given as arguments + self.currentItemChanged.connect(self._slot_changed) + + def _on_sort_indicator_changed(self, _column: int): + self.do_update() + + def set_profile_operations(self, pop: ProfileOperations) -> None: + """Connect an `ProfileOperations` instance and register + event callbacks to it. + """ + self._profile_operations = pop + self._profile_operations.event_dir_added_to_include.register( + self._handle_new_dir_included + ) + + def _handle_new_dir_included(self): + self.do_update() + + def on_now_selected(self): + """Event handler if 'Now' entry in timeline was selected""" + self.do_update(now_selected=True) + + def on_backup_changed(self, _sid): + """Event handler if a backup entry in timeline was selected""" + self.do_update(now_selected=False) + + def do_update(self, now_selected: Optional[bool] = None) -> None: + """Update the places view""" + + # Workaround + if now_selected is None: + now_selected = self.parent.is_now_selected() + + self.clear() + + # name, path, icon + self._add_place(_('Places'), '', '') + self._add_place(_('File System'), '/', 'computer') + + fp_home = pathlib.Path.home() + home_item = self._add_place( + # Use full path in root mode ("/root") otherwise users name only + str(fp_home) if bitbase.IS_IN_ROOT_MODE else fp_home.name, + str(fp_home), + 'user-home') + + # formally known as self.sid + backup_id = self.parent.selected_backup_id() + + # "Now" or a specific snapshot selected? + if now_selected or backup_id is None: + # Use snapshots profiles list of include files and folders + include_entries = self.config.include() + + else: + # Dev note (2026-03): I wonder why this is needed. Isn't the + # profile config stored within each backup. Why not parse + # that config file instead of scanning the real filesystem? + + # Determine directories from the backup itself + base = os.path.expanduser('~') + if not os.path.isdir(backup_id.pathBackup(base)): + # Folder not mounted. We can skip for the next updatePlaces() + return + + folders = [ + i.name + for i + in os.scandir(backup_id.pathBackup(base)) + if i.is_dir() + ] + + include_entries = [(os.path.join(base, f), 0) for f in folders] + + # Use folders only (if 2nd tuple entry is 0) + only_folders = filter(lambda entry: entry[1] == 0, include_entries) + include_folders = [item[0] for item in only_folders] + + if not include_folders: + return + + if not self.header().sortIndicatorSection(): + indic = self.header().sortIndicatorOrder() + reverse = indic == Qt.SortOrder.DescendingOrder + include_folders = sorted(include_folders, reverse=reverse) + + self._add_place(_('Backup directories'), '', '') + + for folder in include_folders: + self._add_place(folder, folder, 'document-save') + + # Select "home" if nothing is selected + if self.currentItem() is None: + self.setCurrentItem(home_item) + + def _add_place(self, name, path, icon) -> QTreeWidgetItem: + """ + Dev note (buhtz, 2024-01-14): Parts of that code are redundant with + timeline.py::HeaderItem.__init__(). + """ + item = QTreeWidgetItem() + + item.setText(0, name) + + if icon: + item.setIcon(0, QIcon.fromTheme(icon)) + + item.setData(0, Qt.ItemDataRole.UserRole, path) + + if not path: + font = item.font(0) + font.setWeight(QFont.Weight.Bold) + item.setFont(0, font) + + # item.setFlags(Qt.ItemFlag.ItemIsEnabled) + item.setFlags(Qt.ItemFlag.NoItemFlags) + item.setForeground( + 0, self.palette().color(QPalette.ColorRole.PlaceholderText)) + item.setBackground( + 0, self.palette().color(QPalette.ColorRole.AlternateBase)) + + self.addTopLevelItem(item) + + if path == self.parent.path: + self.setCurrentItem(item) + + return item + + def _slot_changed(self, item, _previous): + if item is None: + return + + path = str(item.data(0, Qt.ItemDataRole.UserRole)) + if not path: + return + + if path == self.parent.path: + return + + # ??? + self.parent.path = path + self.parent.path_history.append(path) + + self.parent.updateFilesView(3) + + def get_sorting(self) -> tuple[int, int]: + """Current sorting column and order as a tuple.""" + return ( + self.header().sortIndicatorSection(), + self.header().sortIndicatorOrder().value + ) + + def set_sorting(self, sorting: tuple[int, int]) -> None: + """Set sorting.""" + self.header().setSortIndicator(sorting[0], Qt.SortOrder(sorting[1])) diff --git a/qt/plugins/notifyplugin.py b/qt/plugins/notifyplugin.py index 19940a9b9..98fcffc7c 100644 --- a/qt/plugins/notifyplugin.py +++ b/qt/plugins/notifyplugin.py @@ -1,62 +1,73 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2021 Felix Stupp # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -import os +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Notify plugin module""" +import getpass +import dbus import pluginmanager -import subprocess +import logger class NotifyPlugin(pluginmanager.Plugin): - def __init__(self): - self.user = '' + """Plugin used to create notification bubbles in systray. - try: - self.user = os.getlogin() - except: - pass - - if not self.user: - try: - self.user = os.environ['USER'] - except: - pass - - if not self.user: - try: - self.user = os.environ['LOGNAME'] - except: - pass - - def isGui(self): + The plugin use DBUS to send notifications. See its base class for more + details. + """ + + def isGui(self): # noqa: N802 return True - def message(self, profile_id, profile_name, level, message, timeout): - if 1 == level: - cmd = ['notify-send'] - if timeout > 0: - cmd.extend(['-t', str(1000 * timeout)]) - - title = "Back In Time (%s) : %s" % (self.user, profile_name) - message = message.replace("\n", ' ') - message = message.replace("\r", '') - - cmd.append(title) - cmd.append(message) - print(' '.join(cmd)) - subprocess.Popen(cmd).communicate() - return + # pylint: disable-next=too-many-arguments,too-many-positional-arguments + def message(self, + profile_id, + profile_name, + level, + message, + timeout): + # 1 is ERROR, 0 is INFO + if level != 1: + # Dev note (2024-10, buhtz): + # Message with level 0/INFO for example generatet by + # setTakeSnapshotMessage() + # Not clear to me why the notify plugin should only process + # errors. + return + + try: + notify_interface = dbus.Interface( + object=dbus.SessionBus().get_object( + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications"), + dbus_interface="org.freedesktop.Notifications" + ) + + except dbus.exceptions.DBusException as exc: + logger.error('Unexpected DBusException while initiating ' + f'dbus.Interface(): {exc}') + return + + if timeout > 0: + timeout = 1000 * timeout + else: + # let timeout default to notification server settings + timeout = -1 + + title = f'Back In Time ({getpass.getuser()}) : {profile_name}' + message = message.replace('\n', ' ') + message = message.replace('\r', '') + + try: + notify_interface.Notify( + 'Back In Time', 0, '', title, message, [], {}, timeout) + + except dbus.exceptions.DBusException as exc: + logger.error(f'Unexpected DBusException while Notify(): {exc}') diff --git a/qt/plugins/qt4plugin.py b/qt/plugins/qt4plugin.py deleted file mode 100644 index 167e9d6cc..000000000 --- a/qt/plugins/qt4plugin.py +++ /dev/null @@ -1,65 +0,0 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -import sys -import os -import pluginmanager -import tools -import logger -import time -import gettext -import _thread -import subprocess - - -_=gettext.gettext - - -if not os.getenv('DISPLAY', ''): - os.putenv('DISPLAY', ':0.0') - - -class QtPlugin(pluginmanager.Plugin): - def __init__(self): - self.process = None - self.snapshots = None - - def init(self, snapshots): - self.snapshots = snapshots - - if not tools.checkXServer(): - return False - return True - - def isGui(self): - return True - - def processBegin(self): - try: - path = os.path.join(tools.backintimePath('qt'), 'qtsystrayicon.py') - self.process = subprocess.Popen([sys.executable, path, self.snapshots.config.currentProfile()]) - except: - pass - - def processEnd(self): - if not self.process is None: - try: - #self.process.terminate() - return - except: - pass diff --git a/qt/plugins/systrayiconplugin.py b/qt/plugins/systrayiconplugin.py new file mode 100644 index 000000000..bb0a34214 --- /dev/null +++ b/qt/plugins/systrayiconplugin.py @@ -0,0 +1,127 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . + +# Known open issues: +# this script should get started and consider some cmd line arguments from BiT +# (parsed via backintime.createParsers()) so that the same paths are used, +# mainly "share-path" and "config" (path to the config file). +# Otherwise e.g. unit tests or special user path settings may lead to +# wrong status info in the systray icon! +"""Plugin starting the systray icon process + +Dev note (buhtz, 2025-07): Not sure why this need to be a plugin. +""" +import sys +import os +import gettext +import subprocess +import pluginmanager +import tools +import logger + + +_ = gettext.gettext + + +if not os.getenv('DISPLAY', ''): + os.putenv('DISPLAY', ':0.0') + + +class SysTrayIconPlugin(pluginmanager.Plugin): + """A Back In Time plugin responsible to start the systray icon instance""" + + def __init__(self): + self.process = None + self.snapshots = None + + def init(self, snapshots): + self.snapshots = snapshots + + # Old implementation disabled: + # Why can a systray icon only be shown on X11 (not wayland)? + # Qt can handle wayland now! + # if not tools.checkXServer(): + # return False + + # New implementation: Let Qt decide if a system tray icon can be shown. + # See https://doc.qt.io/qt-5/qsystemtrayicon.html#details: + # > To check whether a system tray is present on the user's desktop, + # > call the QSystemTrayIcon::isSystemTrayAvailable() static function. + # + # This requires a QApplication instance (otherwise Qt causes a + # segfault) which we don't have here so we create it to check if a + # window manager ("GUI") is active at all (e.g. in headless + # installations it isn't). + # See: https://forum.qt.io/topic/3852/issystemtrayavailable- + # always-crashes-segfault-on-ubuntu-10-10-desktop/6 + + try: + + if tools.is_Qt_working(systray_required=True): + logger.debug('System tray is available to show the ' + 'BIT system tray icon') + return True + + # pylint: disable-next=broad-exception-caught + except Exception as exc: + logger.debug( + f'Could not ask Qt if system tray is available: {repr(exc)}') + + logger.debug( + 'No system tray available to show the BIT system tray icon') + + return False + + def isGui(self): # noqa: N802 + """True""" + return True + + def processBegin(self): # noqa: N802 + """Start the process.""" + try: + logger.debug('Trying to start systray icon sub process...') + path = os.path.join( + tools.as_backintime_path('qt'), 'qtsystrayicon.py') + cmd = [ + sys.executable, + path, + self.snapshots.config.currentProfile() + ] + + if logger.DEBUG: + # HACK to propagate DEBUG logging level to sub process + cmd.append('--debug') + + # pylint: disable-next=consider-using-with + self.process = subprocess.Popen(cmd) + + # pylint: disable-next=broad-exception-caught + except Exception as exc: + logger.critical(f'Undefined situation: {exc}', self) + + else: + logger.info('Systray icon sub process started.') + + # def processEnd(self): + # """Dev note(2025-07, buhtz): Method makes no sense to me anymore. + # Remove it soon. + # """ + # if self.process is not None: + # try: + # # The "qtsystrayicon.py" app does terminate itself + # # once the snapshot has been taken so there is no need + # # to do anything here to stop it or clean-up anything. + # # self.process.terminate() + # return + + # # ??? + # except: + # pass diff --git a/qt/profile_operations.py b/qt/profile_operations.py new file mode 100644 index 000000000..cfb503c88 --- /dev/null +++ b/qt/profile_operations.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: © 2026 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Provide operations related to manipulating backup profiles. + +This module contains the application logic independent from the GUI layer. + +Dev note (buhtz, 2026-02): Copeling to the config module will be refactored +soon. See PR #1850 +""" +from pathlib import Path +from event import Event + + +class ProfileOperations: + """Profile related operations""" + event_dir_added_to_include = Event() + + def __init__(self, profile_id, config): + self._profile_id = profile_id + self._config = config + + def _split_duplicates(self, + existing: list[str], + new_paths: list[str] + ) -> tuple[list[str], list[str]]: + """Split `new_paths` into duplicates and new entries. + + Args: + existing: List of paths already existing paths. + new_paths: List of paths to check. + + Returns: + A 2-entry tuple with lists of duplicated and new existing + items. + """ + duplicates, to_add = [], [] + for val in new_paths: + (duplicates if val in existing else to_add).append(val) + + return duplicates, to_add + + # def get_includes(self) -> list: + # """Return the list of include entries.""" + # includes = self._config.include(profile_id=self._profile_id) + + def add_include(self, paths: list[str] | list[Path]) -> None: + """Add entries to the include list. + + Returns: List of duplicates otherwise empty list. + """ + # list of tuples; ('path/', 0) + includes = self._config.include(profile_id=self._profile_id) + + duplicates, to_add = self._split_duplicates( + [val for val, _ in includes], + paths + ) + + contains_dir = False + for fp in to_add: + # 0 = directory, 1 = file + if Path(fp).is_dir(): + type_mod = 0 + contains_dir = True + else: + type_mod = 1 + includes.append((fp, type_mod)) + + self._config.setInclude(includes, profile_id=self._profile_id) + + if contains_dir: + self.event_dir_added_to_include.notify() + + return duplicates + + def add_exclude(self, paths: list[str]) -> list[str]: + """Add entries to the exclude list. + + Returns: List of duplicates otherwise an empty list. + """ + excludes = self._config.exclude(profile_id=self._profile_id) + duplicates, to_add = self._split_duplicates(excludes, paths) + excludes.extend(to_add) + self._config.setExclude(excludes, profile_id=self._profile_id) + + return duplicates diff --git a/qt/qtsystrayicon.py b/qt/qtsystrayicon.py index 4c7a0646a..eb085ca2e 100644 --- a/qt/qtsystrayicon.py +++ b/qt/qtsystrayicon.py @@ -1,112 +1,171 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2025 Christian Buhtz +# SPDX-FileCopyrightText: © 2025 Gregory Deseck # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Separate application managing the systray icon""" import sys import os -import gettext +import re +import pwd +import json import subprocess import signal - -_=gettext.gettext - +import textwrap +import functools +from typing import Callable +# TODO Is this really required? If the client is not configured for X11 +# it may use Wayland or something else... +# Or is this just required when run as root (where GUIs are not +# configured normally)? if not os.getenv('DISPLAY', ''): os.putenv('DISPLAY', ':0.0') import qttools -qttools.registerBackintimePath('common') - -import tools +qttools.register_backintime_path('common') import logger +# Workaround until the codebase allows a single place to init all translations +import tools +tools.initiate_translation(None) import snapshots import progress import logviewdialog import encfstools +from PyQt6.QtCore import QTimer +from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QProgressBar, QWidget +from PyQt6.QtGui import QIcon, QRegion + + +def trust_required(method: Callable) -> Callable: + """Decorator to allow execution only if _trust_desktop is True.""" + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + if self._trust_desktop: + return method(self, *args, **kwargs) -from PyQt5.QtCore import QObject, QTimer -from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QProgressBar, QWidget -from PyQt5.QtGui import QIcon, QRegion + return None + + return wrapper class QtSysTrayIcon: + """Application instance for the Back In Time systray icon""" + + # pylint: disable-next=line-too-long + ICON_PATH_ONLY = '\n' + # pylint: disable-next=line-too-long + ICON_PART_A = '' + def __init__(self): + self.snapshots = snapshots.Snapshots() self.config = self.snapshots.config self.decode = None + self._current_user = pwd.getpwuid(os.getuid()).pw_name + if len(sys.argv) > 1: if not self.config.setCurrentProfile(sys.argv[1]): - logger.warning("Failed to change Profile_ID %s" - %sys.argv[1], self) + logger.warning( + f'Failed to change Profile_ID {sys.argv[1]}', self) - self.qapp = qttools.createQApplication(self.config.APP_NAME) - translator = qttools.translator() + self.qapp = qttools.create_qapplication(self.config.APP_NAME) + translator = qttools.initiate_translator(self.config.language()) self.qapp.installTranslator(translator) self.qapp.setQuitOnLastWindowClosed(False) import icon - self.icon = icon self.qapp.setWindowIcon(icon.BIT_LOGO) - self.status_icon = QSystemTrayIcon(icon.BIT_LOGO) - #self.status_icon.actionCollection().clear() + self.status_icon = self._create_status_icon() self.contextMenu = QMenu() - self.menuProfileName = self.contextMenu.addAction(_('Profile: "%s"') % self.config.profileName()) - qttools.setFontBold(self.menuProfileName) + # The systray icon instance runs as the same user and with similar + # privilegs as the BIT instance itself; e.g. root. + # We need to know which user "owns" the desktop. If it is another + # user, the systray instance should not expose sensible data like + # paths of files and dirs in the backup. + desktop_user = self._determine_desktop_session_user() + + self._trust_desktop = desktop_user == self._current_user + + if self._trust_desktop: + logger.info('Trusting the desktop session.') + else: + logger.info( + 'Not trusting the desktop session, which is owned by ' + f'"{desktop_user}". ' + f'But backup runs as "{self._current_user}".') + + if self._trust_desktop: + txt = _('Profile: {profile_name}').format( + profile_name=self.config.profileName()) + else: + txt = _('Profile: {profile_name} (by user "{desktop_user}")') \ + .format(profile_name=self.config.profileName(), + desktop_user=desktop_user) + self.menuProfileName = self.contextMenu.addAction(txt) self.contextMenu.addSeparator() self.menuStatusMessage = self.contextMenu.addAction(_('Done')) + self.menuProgress = self.contextMenu.addAction('') self.menuProgress.setVisible(False) self.contextMenu.addSeparator() - self.btnPause = self.contextMenu.addAction(icon.PAUSE, _('Pause snapshot process')) - action = lambda: os.kill(self.snapshots.pid(), signal.SIGSTOP) - self.btnPause.triggered.connect(action) + self.btnPause = self.contextMenu.addAction( + icon.PAUSE, _('Pause backup process')) + self.btnPause.triggered.connect(self._slot_pause) + self.btnPause.setVisible(self._trust_desktop) - self.btnResume = self.contextMenu.addAction(icon.RESUME, _('Resume snapshot process')) - action = lambda: os.kill(self.snapshots.pid(), signal.SIGCONT) - self.btnResume.triggered.connect(action) + self.btnResume = self.contextMenu.addAction( + icon.RESUME, _('Resume backup process')) + self.btnResume.triggered.connect(self._slot_resume) self.btnResume.setVisible(False) - self.btnStop = self.contextMenu.addAction(icon.STOP, _('Stop snapshot process')) + self.btnStop = self.contextMenu.addAction( + icon.STOP, _('Stop backup process')) self.btnStop.triggered.connect(self.onBtnStop) + self.btnStop.setVisible(self._trust_desktop) self.contextMenu.addSeparator() - self.btnDecode = self.contextMenu.addAction(icon.VIEW_SNAPSHOT_LOG, _('decode paths')) + # Dev note (2025-12, buhtz): I wondered why this decode checkbox is + # visible only in ssh_encfs mode but not in local_encfs. Still not + # sure but I think the reason is that in ssh_encfs there is no + # "double-mounting": First SSH and then EncFS. In consequence this + # decode button is a workaround. Explicit decoding in local_encfs + # is not necessary because this happens via encfs-mounting. This + # step is missing when using ssh_encfs. + self.btnDecode = self.contextMenu.addAction( + icon.VIEW_SNAPSHOT_LOG, _('decode paths')) self.btnDecode.setCheckable(True) - self.btnDecode.setVisible(self.config.snapshotsMode() == 'ssh_encfs') + self.btnDecode.setVisible( + self.config.snapshotsMode() == 'ssh_encfs' and self._trust_desktop) self.btnDecode.toggled.connect(self.onBtnDecode) - self.openLog = self.contextMenu.addAction(icon.VIEW_LAST_LOG, _('View Last Log')) + self.openLog = self.contextMenu.addAction( + icon.VIEW_LAST_LOG, _('View Last Log')) self.openLog.triggered.connect(self.onOpenLog) - self.startBIT = self.contextMenu.addAction(icon.BIT_LOGO, _('Start BackInTime')) + self.openLog.setVisible(self._trust_desktop) + + self.startBIT = self.contextMenu.addAction( + icon.BIT_LOGO, + _('Start {appname}').format(appname=self.config.APP_NAME) + ) self.startBIT.triggered.connect(self.onStartBIT) + self.startBIT.setVisible(self._trust_desktop) + self.status_icon.setContextMenu(self.contextMenu) - self.pixmap = icon.BIT_LOGO.pixmap(24) - self.progressBar = QProgressBar() - self.progressBar.setMinimum(0) - self.progressBar.setMaximum(100) - self.progressBar.setValue(0) - self.progressBar.setTextVisible(False) - self.progressBar.resize(24, 6) - self.progressBar.render(self.pixmap, sourceRegion = QRegion(0, -14, 24, 6), flags = QWidget.RenderFlags(QWidget.DrawChildren)) + self.progressBar = self._create_progress_bar() self.first_error = self.config.notify() self.popup = None @@ -115,7 +174,42 @@ def __init__(self): self.timer = QTimer() self.timer.timeout.connect(self.updateInfo) - def prepairExit(self): + def _create_status_icon(self) -> QSystemTrayIcon: + # Logo color depending on dark/light mode + mode = self.config.systray() + + if mode == 'light': + return QSystemTrayIcon(self.get_light_icon()) + + if mode == 'dark': + return QSystemTrayIcon(self.get_dark_icon()) + + if qttools.in_dark_mode(self.qapp): + return QSystemTrayIcon(self.get_light_icon()) + + return QSystemTrayIcon(self.get_dark_icon()) + + def _create_progress_bar(self) -> QProgressBar: + bar = QProgressBar() + + bar.setMinimum(0) + bar.setMaximum(100) + bar.setValue(0) + + bar.setTextVisible(False) + + bar.resize(24, 6) + + import icon + bar.render( + icon.BIT_LOGO.pixmap(24), + sourceRegion=QRegion(0, -14, 24, 6), + flags=QWidget.RenderFlag.DrawChildren + ) + + return bar + + def prepareExit(self): self.timer.stop() if not self.status_icon is None: @@ -129,99 +223,306 @@ def prepairExit(self): self.qapp.processEvents() def run(self): - if not self.snapshots.busy(): - sys.exit() + if '--keep-alive' not in sys.argv: + if not self.snapshots.busy(): + sys.exit() + self.status_icon.show() self.timer.start(500) - - logger.debug("begin loop", self) - - self.qapp.exec_() - - logger.debug("end loop", self) - - self.prepairExit() + self.qapp.exec() + self.prepareExit() def updateInfo(self): - if not self.snapshots.busy(): - self.prepairExit() - self.qapp.exit(0) - return + # Exit this systray icon "app" when the snapshots is taken + if '--keep-alive' not in sys.argv: + if not self.snapshots.busy(): + self.prepareExit() + self.qapp.exit(0) + return paused = tools.processPaused(self.snapshots.pid()) - self.btnPause.setVisible(not paused) - self.btnResume.setVisible(paused) + self.btnPause.setVisible(not paused and self._trust_desktop) + self.btnResume.setVisible(paused and self._trust_desktop) + + if self._trust_desktop: + message = self.snapshots.takeSnapshotMessage() + else: + message = None - message = self.snapshots.takeSnapshotMessage() if message is None and self.last_message is None: - message = (0, _('Working...')) + message = (0, _('Working…')) if not message is None: if message != self.last_message: self.last_message = message + if self.decode: message = (message[0], self.decode.log(message[1])) - self.menuStatusMessage.setText('\n'.join(tools.wrapLine(message[1],\ - size = 80,\ - delimiters = '',\ - new_line_indicator = '') \ - )) + + self.menuStatusMessage.setText( + '\n'.join(textwrap.wrap(message[1], width=80))) + self.status_icon.setToolTip(message[1]) pg = progress.ProgressFile(self.config) + if pg.fileReadable(): pg.load() - percent = pg.intValue('percent') + # percent = pg.intValue('percent') ## disable progressbar in icon until BiT has it's own icon ## fixes bug #902 # if percent != self.progressBar.value(): # self.progressBar.setValue(percent) - # self.progressBar.render(self.pixmap, sourceRegion = QRegion(0, -14, 24, 6), flags = QWidget.RenderFlags(QWidget.DrawChildren)) + # self.progressBar.render( + # self.pixmap, + # sourceRegion=QRegion(0, -14, 24, 6), + # flags=QWidget.RenderFlags(QWidget.DrawChildren)) # self.status_icon.setIcon(QIcon(self.pixmap)) self.menuProgress.setText(' | '.join(self.getMenuProgress(pg))) self.menuProgress.setVisible(True) + else: # self.status_icon.setIcon(self.icon.BIT_LOGO) self.menuProgress.setVisible(False) def getMenuProgress(self, pg): - d = (('sent', _('Sent:')), \ - ('speed', _('Speed:')),\ - ('eta', _('ETA:'))) - for key, txt in d: + """See common/app.py::MainWindow.getProgressBarFormat(). + + The code is a near duplicate. + """ + data = ( + ('sent', _('Sent:')), + ('speed', _('Speed:')), + ('eta', _('ETA:')) + ) + + for key, txt in data: value = pg.strValue(key, '') + if not value: continue + yield txt + ' ' + value - def onStartBIT(self): + @trust_required + def _slot_pause(self, *_args, **_kwargs): + os.kill(self.snapshots.pid(), signal.SIGSTOP) + + @trust_required + def _slot_resume(self, *_args, **_kwargs): + os.kill(self.snapshots.pid(), signal.SIGCONT) + + @trust_required + def onStartBIT(self, *_args, **_kwargs): profileID = self.config.currentProfile() cmd = ['backintime-qt',] if not profileID == '1': - cmd += ['--profile-id', profileID] - proc = subprocess.Popen(cmd) - - def onOpenLog(self): - dlg = logviewdialog.LogViewDialog(self, systray = True) - dlg.decode = self.decode - dlg.cbDecode.setChecked(self.btnDecode.isChecked()) - dlg.exec_() - - def onBtnDecode(self, checked): + cmd += ['--profile', profileID] + _proc = subprocess.Popen(cmd) + + @trust_required + def onOpenLog(self, *_args, **_kwargs): + dlg = logviewdialog.LogViewDialog( + parent=self, + decode=self.btnDecode.isChecked()) + dlg.exec() + + @trust_required + def onBtnDecode(self, checked, *_args, **_kwargs): if checked: self.decode = encfstools.Decode(self.config) self.last_message = None self.updateInfo() - else: - self.decode = None + return + + self.decode = None - def onBtnStop(self): + @trust_required + def onBtnStop(self, *_args, **_kwargs): os.kill(self.snapshots.pid(), signal.SIGKILL) self.btnStop.setEnabled(False) self.btnPause.setEnabled(False) self.btnResume.setEnabled(False) - self.snapshots.setTakeSnapshotMessage(0, 'Snapshot terminated') + self.snapshots.setTakeSnapshotMessage(0, 'Backup terminated') + + def _get_desktop_user_via_loginctl(self) -> str | None: + """Get name of user logged in to the current desktop session using + loginctl. + """ + + try: + # get list of sessions + cmd = ['loginctl', 'list-sessions', '--no-legend', '--json=short'] + # logger.debug(f'Execute {cmd=}') + output = subprocess.check_output(cmd, text=True) + + except FileNotFoundError: + logger.warning( + 'Can not determine user name of current desktop ' + 'session because "loginctl" is not available.' + ) + return None + + except Exception as exc: + logger.error( + 'Unexpected error while determining user name of ' + f'current desktop session: {exc}' + ) + return None + + sessions = [] + + # Check each session + for session in json.loads(output): + # logger.debug(f'{session=}') + # Ignore none-user sessions + if session.get('class') != 'user': + continue + + # properties of the session + info = subprocess.check_output( + [ + 'loginctl', + 'show-session', + str(session['session']), + '--property=Active', + '--property=Name', + '--property=Seat', + '--property=Type', + '--property=Display', + ], + text=True + ).strip() + # logger.debug(f'{info=}') + + props = dict(line.split('=', 1) for line in info.splitlines()) + # logger.debug(f'{props=}') + sessions.append(props) + + # logger.debug(f'{sessions=}') + + display = os.environ.get('DISPLAY') + # logger.debug(f'{display=}') + + if display: + display = display.split('.')[0] + + matches = [ + s for s in sessions + if s.get('Display', '').split('.')[0] == display + ] + # logger.debug(f'{matches=}') + + if len(matches) == 1: + logger.debug( + 'User determined via loginctl using DISPLAY', self) + return matches[0].get('Name') + + return None + + # Fallback checking for one active session, if DISPLAY not set + fallback = [ + s for s in sessions + if s.get('Active', '').lower() == 'yes' + and s.get('Seat') == 'seat0' + and s.get('Type') in ('x11', 'wayland') + ] + + if len(fallback) == 1: + logger.debug( + 'User determined via loginctl fallback (DISPLAY is not set)', + self) + return fallback[0].get('Name') + + return None + + def _get_desktop_user_via_x11_who(self) -> str | None: + """Using 'who' to determine the current DISPLAY's user. + + The output of 'who' can look like this: + + user sshd pts/0 2025-09-16 08:30 (fe80::d65:ea81:c46f:7f0d%eth0) + lightdm seat0 2025-09-15 16:07 (:0) + """ + if not os.environ.get('DISPLAY'): + return None + + try: + # list of users logged in + output = subprocess.check_output(['who'], text=True).strip() + except Exception as exc: + logger.error( + 'Unexpected error while determining user name of ' + f'current desktop session: {exc}' + ) + return None + + display = os.environ.get('DISPLAY', ':0').split('.')[0] + + # each user + for line in output: + found = re.match(r'^(\S+).*\((.*)\)$', line) + + if not found: + continue + + user, userdisplay = found.groups() + userdisplay = userdisplay.split('.')[0] + + if userdisplay == display: + logger.debug('User determined via x11 who', self) + return user + + return None + + def _determine_desktop_session_user(self): + """Return name of user logged in to the current desktop session. + """ + logger.info( + 'Try to determine user of current desktop session via loginctl') + + user = self._get_desktop_user_via_loginctl() + + if not user: + logger.info( + 'Try to determine user of current desktop session via x11-who') + user = self._get_desktop_user_via_x11_who() + + logger.info( + f'Systray Icon determined the user "{user}" as owner of ' + 'current desktop session.') + + return user + + @classmethod + def _get_icon_filled(cls, color: str) -> QIcon: + """Generate the dark symbolic icon""" + svg_content = cls.ICON_PART_A + f' fill="{color}"' + cls.ICON_PART_B + qicon = qttools.create_qicon_from_svg_source(svg_content) + return qicon + + @staticmethod + def get_dark_icon() -> QIcon: + return QtSysTrayIcon._get_icon_filled('black') + + @staticmethod + def get_light_icon() -> QIcon: + return QtSysTrayIcon._get_icon_filled('white') + if __name__ == '__main__': + # Use '--keep-alive' to keep the systray icon alive. This is for debug + # purpose only. + + logger.openlog('SYSTRAY') + + # HACK: Minimal arg parsing to enable debug-level logging + if '--debug' in sys.argv: + logger.DEBUG = True + + logger.debug( + f'Systray icon process (PID: {os.getpid()} User: {logger.USER}) ' + f'called with {sys.argv}') + QtSysTrayIcon().run() diff --git a/qt/qttools.py b/qt/qttools.py index a7517b052..d7586c7ac 100644 --- a/qt/qttools.py +++ b/qt/qttools.py @@ -1,450 +1,711 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Some helper functions and additional classes in context of Qt. + + - Helpers for Qt Fonts. + - Helpers about path manipulation. + - FiledialogShowHidden + - Menu (tooltips in menus) + - etc +""" +# pylint: disable=wrong-import-position,wrong-import-order import os import sys -import gettext -from PyQt5.QtGui import (QFont, QColor, QKeySequence) -from PyQt5.QtCore import (QDir, Qt, pyqtSlot, pyqtSignal, QModelIndex, - QTranslator, QLocale, QLibraryInfo, QEvent, - QT_VERSION_STR) -from PyQt5.QtWidgets import (QFileDialog, QAbstractItemView, QListView, - QTreeView, QDialog, QApplication, QStyleFactory, - QTreeWidget, QTreeWidgetItem, QComboBox, QMenu, - QToolTip, QAction) -from datetime import (datetime, date, timedelta) -from calendar import monthrange -from distutils.version import StrictVersion - -_ = gettext.gettext - -def backintimePath(*path): - return os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, *path)) - -def registerBackintimePath(*path): +import atexit +import pwd +import re +import json +import textwrap +import subprocess +import tempfile +from typing import Union, Iterable, Callable +from pathlib import Path +from contextlib import contextmanager +from textdlg import TextDialog +from PyQt6.QtGui import (QCursor, + QDesktopServices, + QGuiApplication, + QFontMetricsF, + QIcon, + QPalette) +from PyQt6.QtCore import (QEvent, + QLibraryInfo, + QLocale, + Qt, + QObject, + QTranslator, + QUrl) +from PyQt6.QtWidgets import (QApplication, + QHBoxLayout, + QLabel, + QStyle, + QStyleFactory, + QSystemTrayIcon, + QToolTip, + QWidget) +from qttools_path import register_backintime_path +register_backintime_path('common') +import tools # noqa: E402 +import logger # noqa: E402 +import bitbase # noqa: E402 +import version # noqa: E402 +import messagebox # noqa: E402 + + +_DARK_MODE_THRESHOLD = 128 + +# |--------------------------------| +# | Widget modification & creation | +# |--------------------------------| + + +def can_render(string, widget): + """Check if the string can be rendered by the font used by the widget. + + Args: + string(str): The string to check. + widget(QWidget): The widget which font is used. + + Returns: + (bool) True if the widgets font contain all given characters. + """ + fm = widget.fontMetrics() + + for c in string: + # Convert the unicode character to its integer representation + # because fm.inFont() is not able to handle 2-byte characters + if not fm.inFontUcs4(ord(c)): + return False + + return True + + +def in_dark_mode(widget_or_application: QWidget | QApplication) -> bool: + """Determine if the desktop/theme is in dark mode.""" + palette = widget_or_application.palette() + + window_color = palette.color(QPalette.ColorRole.Window) + + return window_color.value() < _DARK_MODE_THRESHOLD + + +_REX_RICHTEXT = re.compile( + # begin of line + r'^' + # all characters, except a new line + r'[^\n]*' + # tag opening + r'<' + # every character (as tagname) except > + r'[^>]+' + # tag closing + r'>') + + +def might_be_richtext(txt: str) -> bool: + """Returns `True` if the text is rich text. + + Rich text is a subset of HTML used by Qt to allow text formatting. The + function checks if the first line (before the first `\n') does contain a + tag. A tag begins with with `<`, following by one or more characters and + close with `>`. + + Qt itself does use `Qt::mightBeRichText()` internally but this is not + available in PyQt for unknown reasons. + + Args: + txt: The text to check. + + Returns: + `True` if it looks like a rich text, otherwise `False`. + """ + return bool(_REX_RICHTEXT.match(txt)) + + +def set_wrapped_tooltip(widget: Union[QWidget, Iterable[QWidget]], + tooltip: Union[str, Iterable[str]], + wrap_length: int = 72): + """Add a tooltip to the widget but insert line breaks when appropriated. + + If a list of strings is provided, each string is wrapped individually and + then joined with a line break. + + Args: + widget: The widget or list of widgets to which a tooltip should be + added. + tooltip: The tooltip as string or iterable of strings. + wrap_length: Every line is at most this lengths. + """ + + if isinstance(widget, Iterable): + for wdg in widget: + set_wrapped_tooltip(wdg, tooltip, wrap_length) + + return + + # Always use tuple or list + if isinstance(tooltip, str): + tooltip = (tooltip, ) + + # Richtext or plain text + is_richtext = might_be_richtext(tooltip[0]) + + newline = {True: '
', False: '\n'}[is_richtext] + + result = [] + # Wrap each paragraph in itself + for paragraph in tooltip: + result.append('\n'.join( + textwrap.wrap(paragraph, wrap_length) + )) + + # glue all together + result = newline.join(result) + + # Qt handles the string as richttext (interpreting html tags) only, + # if it begins with a tag. + if is_richtext and result[0] != '<': + result = f'{result}' + + widget.setToolTip(result) + + +def update_combo_profiles(config, combo_profiles, current_profile_id): """ - find duplicate in common/tools.py + Updates the combo box with profiles. + + :param config: Configuration object with access to profile data. + :param combo_profiles: The combo box widget to be updated. + :param current_profile_id: The ID of the current profile to be selected. """ - path = backintimePath(*path) - if not path in sys.path: - sys.path.insert(0, path) - -registerBackintimePath('common') -import snapshots - -def fontBold(font): - font.setWeight(QFont.Bold) - return font - -def setFontBold(widget): - widget.setFont(fontBold(widget.font())) - -def fontNormal(font): - font.setWeight(QFont.Normal) - return font - -def setFontNormal(widget): - widget.setFont(fontNormal(widget.font())) - -def equalIndent(*args): - width = 0 - for widget in args: - widget.setMinimumWidth(0) - width = max(width, widget.sizeHint().width()) - if len(args) > 1: - for widget in args: - widget.setMinimumWidth(width) - -class FileDialogShowHidden(QFileDialog): - def __init__(self, parent, *args, **kwargs): - super(FileDialogShowHidden, self).__init__(parent, *args, **kwargs) - self.setOption(self.DontUseNativeDialog, True) - self.setOption(self.HideNameFilterDetails, True) - - showHiddenAction = QAction(self) - showHiddenAction.setShortcuts([QKeySequence(Qt.CTRL + Qt.Key_H),]) - showHiddenAction.triggered.connect(self.toggleShowHidden) - self.addAction(showHiddenAction) - - self.showHidden(hiddenFiles(parent)) - - def showHidden(self, enable): - if enable: - self.setFilter(self.filter() | QDir.Hidden) - elif int(self.filter() & QDir.Hidden): - self.setFilter(self.filter() ^ QDir.Hidden) - - def toggleShowHidden(self): - self.showHidden(not int(self.filter() & QDir.Hidden)) - -def getExistingDirectories(parent, *args, **kwargs): + profiles = config.profilesSortedByName() + for profile_id in profiles: + combo_profiles.add_profile_id(profile_id) + if profile_id == current_profile_id: + combo_profiles.set_current_profile_id(profile_id) + + +def create_icon_label( + icon_type: QStyle.StandardPixmap, + icon_size: QStyle.PixelMetric = QStyle.PixelMetric.PM_LargeIconSize, + icon_scale_factor: float | int = None, + fixed_size_widget: bool = False) -> QLabel: + """Return a ``QLabel`` instance containing an icon. + + Args: + icon_type: The icon, eg. info or warning. + icon_size: Size reference. + fixed_size_widget: Fix label size to its icon (default: False) + + Returns: + The QLabel """ - Workaround for selecting multiple directories - adopted from http://www.qtcentre.org/threads/34226-QFileDialog-select-multiple-directories?p=158482#post158482 - This also give control about hidden folders + style = QApplication.style() + ico = style.standardIcon(icon_type) + sz = style.pixelMetric(icon_size) + + if icon_scale_factor: + sz = int(sz * icon_scale_factor) + + pixmap = ico.pixmap(sz) + + label = QLabel() + label.setPixmap(pixmap) + + if fixed_size_widget: + label.setFixedSize(pixmap.size()) + + return label + + +def create_icon_label_info( + icon_size: QStyle.PixelMetric = QStyle.PixelMetric.PM_LargeIconSize, + icon_scale_factor: float | int = None, + fixed_size_widget: bool = False) -> QLabel: + """Return a QLabel with an info icon. + + See `create_icon_label` for details. """ - dlg = FileDialogShowHidden(parent, *args, **kwargs) - dlg.setFileMode(dlg.Directory) - dlg.setOption(dlg.ShowDirsOnly, True) - dlg.findChildren(QListView)[0].setSelectionMode(QAbstractItemView.ExtendedSelection) - dlg.findChildren(QTreeView)[0].setSelectionMode(QAbstractItemView.ExtendedSelection) + return create_icon_label( + icon_type=QStyle.StandardPixmap.SP_MessageBoxInformation, + icon_size=icon_size, + icon_scale_factor=icon_scale_factor, + fixed_size_widget=fixed_size_widget) - if dlg.exec_() == QDialog.Accepted: - return dlg.selectedFiles() - return [str(), ] -def getExistingDirectory(parent, *args, **kwargs): +def create_icon_label_warning( + icon_size: QStyle.PixelMetric = QStyle.PixelMetric.PM_LargeIconSize, + icon_scale_factor: float | int = None, + fixed_size_widget: bool = False) -> QLabel: + """Return a QLabel with a warning icon. + + See `create_icon_label` for details. """ - Workaround to give control about hidden folders + return create_icon_label( + icon_type=QStyle.StandardPixmap.SP_MessageBoxWarning, + icon_size=icon_size, + icon_scale_factor=icon_scale_factor, + fixed_size_widget=fixed_size_widget) + + +def create_info_label( + text: str, + icon_size: QStyle.PixelMetric = QStyle.PixelMetric.PM_LargeIconSize, + icon_scale_factor: float | int = None, + fixed_size_widget: bool = True) -> QLabel: + """Return a widget with an info icon and text. + + See `create_icon_label` for details. """ - dlg = FileDialogShowHidden(parent, *args, **kwargs) - dlg.setFileMode(dlg.Directory) - dlg.setOption(dlg.ShowDirsOnly, True) + ico = create_icon_label_info( + icon_size, icon_scale_factor, fixed_size_widget) + + return _combine_icon_with_label(ico, text) - if dlg.exec_() == QDialog.Accepted: - return dlg.selectedFiles()[0] - return str() -def getOpenFileNames(parent, *args, **kwargs): +def create_warning_label( + text: str, + icon_size: QStyle.PixelMetric = QStyle.PixelMetric.PM_LargeIconSize, + icon_scale_factor: float | int = None, + fixed_size_widget: bool = True) -> QLabel: + """Return a widget with a warning icon and text. + + See `create_icon_label` for details. """ - Workaround to give control about hidden files + ico = create_icon_label_warning( + icon_size, icon_scale_factor, fixed_size_widget) + + return _combine_icon_with_label(ico, text) + + +def _combine_icon_with_label(icon: QLabel, text: str) -> QWidget: + """Horizontally combine the given `icon` with a text label + + Args: + icon: A `QLabel` containing an icon used as first item in the + horizontal layout. + text: The text used in a `Qlabel` as second item in the horizontal + layout. + + Returns: + A widget with horizontal layout containing an icon label and a text + label. """ - dlg = FileDialogShowHidden(parent, *args, **kwargs) - dlg.setFileMode(dlg.ExistingFiles) + txt = QLabel(text) + txt.setWordWrap(True) - if dlg.exec_() == QDialog.Accepted: - return dlg.selectedFiles() - return [str(), ] + # Show URL in tooltip without anoing http-protocol prefix. + if ' QIcon: + """Create a QIcon instance based on SVG/XML source. + + QIcon is not capable of reading from a byte stream. + This workaround write the SVG/XML-string to a temporary in-RAM file + before QIcon reads it back. """ - Workaround to give control about hidden files + + svg_fn = None + + with tempfile.NamedTemporaryFile(suffix='.svg', mode='w', delete=False + ) as handle: + handle.write(svg_source) + svg_fn = handle.name + + atexit.register(Path(svg_fn).unlink) + + return QIcon(svg_fn) + + +def custom_sort_order(header, loop, new_column, new_order): + """Provides a toggled sort order across two columns for QTreeWidget.""" + if new_column == 0 and new_order == Qt.SortOrder.AscendingOrder: + if loop: + new_column, new_order = 1, Qt.SortOrder.AscendingOrder + header.setSortIndicator(new_column, new_order) + loop = False + else: + loop = True + header.model().sort(new_column, new_order) + return loop + +# |---------------------| +# | Misc / Uncatgorized | +# |---------------------| + + +class MouseButtonEventFilter(QObject): + """Catch mouse buttons 4 and 5 (mostly used as back and forward) + and assign it to browse in file history. """ - dlg = FileDialogShowHidden(parent, *args, **kwargs) - dlg.setFileMode(dlg.ExistingFile) - if dlg.exec_() == QDialog.Accepted: - return dlg.selectedFiles()[0] - return str() + def __init__(self, + back_handler: Callable, + forward_handler: Callable): + # mouse button 4 + self._handle_back = back_handler + # mouse button 5 + self._handle_forward = forward_handler + + super().__init__() + + # pylint: disable-next=invalid-name + def eventFilter(self, receiver: QObject, event: QEvent): # noqa: N802 + """Catch global input events.""" + + # not a mouse press event + if event.type() != QEvent.Type.MouseButtonPress: + return super().eventFilter(receiver, event) -def hiddenFiles(parent): + try: + # button 4 or 5 ? + handler = { + Qt.MouseButton.BackButton: self._handle_back, + Qt.MouseButton.ForwardButton: self._handle_forward + }[event.button()] + + except KeyError: + pass + + else: # no exception + handler() + return True + + return super().eventFilter(receiver, event) + + +def _determine_root_mode_user() -> str: + """Determine the users name who started BIT in root mode + + Usually pkexec is used but some users do use sudo themself. + """ + # Try pkexec + uid = os.getenv('PKEXEC_UID', None) + if uid: + return pwd.getpwuid(int(uid)).pw_name + + # Try sudo + sudo_user = os.getenv('SUDO_USER', None) + if sudo_user: + return sudo_user + + return None + + +def open_url(url: str) -> None: + """Open a URL with the systems default browser using xdg-open.""" + # regular user mode + if not bitbase.IS_IN_ROOT_MODE: + QDesktopServices.openUrl(QUrl(url)) + return + + # root mode + user_name = _determine_root_mode_user() + + try: + subprocess.run( + [ + 'runuser', + '-u', + user_name, + '--', + 'xdg-open', + url + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + + except (subprocess.CalledProcessError, FileNotFoundError) as exc: + logger.error(f'Problem while opening "{url}" as user ' + f'"{user_name}" while in root-mode. ' + f'Error was: {exc}') + + except Exception as exc: # pylint: disable=broad-exception-caught + logger.critical(f'Unknown problem while opening "{url}" as user ' + f'"{user_name}" while in root-mode. ' + f'Error was: {exc}') + + +def screen_width_in_chars(widget: QWidget, reference_char: str = 'M') -> int: + """Width of the screen in number of characters ('em'). + + The calculation is based on the width of one character, determined via + font metrics regarding the given widget. + """ + # Calculate width in pixel for one 'em' (letter 'M') + metrics = QFontMetricsF(widget.font()) + char_px = metrics.horizontalAdvance(reference_char) + + # Screen width + handle = widget.windowHandle() + screen = handle.screen() if handle else QGuiApplication.primaryScreen() + geom = screen.availableGeometry() + + # Screen width in 'em' (number of characters) + return int(geom.width() / char_px) + + +def open_man_page(manpage: str, + icon: QIcon = None, + section: str = None) -> None: + """Open the manpage in a text browser window. + + The position and geometry of the dialog depends on fractions of the screen. + The the man page content's linebreaks in the dialog are adjusted to the + width of the dialog. + + Args: + manpage: Name of the manpage. + icon: Icon to use for the window. + section: Section of the man page to scroll to. + """ + + content = '' + + if section: + # Search for one line containing only the section heading but + # allow blanks before and behind it. + pattern = r'(?m)^\s*' + section + r'\s*$' + else: + pattern = None + + # The dialog + td = TextDialog( + content, + markdown=False, + scroll_to=pattern, + title=_('man page: {man_page_name}').format( + man_page_name=manpage), + icon=icon + ) + + # Determine man page width in characters... + chars = screen_width_in_chars(td.browser_widget) + # ...using text dialogs screen fraction and a 5% security buffer + chars = int(chars * td.width_fraction * 0.95) + + env = os.environ.copy() + env['MANWIDTH'] = f'{chars}' + + # Get content from man page try: - return parent.parent.showHiddenFiles - except: pass + proc = subprocess.run( + ['man', manpage], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + env=env, + check=False + ) + content = proc.stdout + + if not content: + raise FileNotFoundError( + f'No content for man page "{manpage}".\n' + f'Error: {proc.stderr.strip()} ({proc.returncode})' + ) + + except FileNotFoundError as exc: + messagebox.critical(None, str(exc)) + logger.error(str(exc)) + return + + # Show dialog with content + td.set_content(content) + td.exec() + + +def user_manual_uri() -> str: + """Return the URI to the user manual. + + If available the local URI is used otherwise the online version is. + """ + uri = bitbase.USER_MANUAL_LOCAL_PATH.as_uri() \ + if bitbase.USER_MANUAL_LOCAL_AVAILABLE \ + else bitbase.URL_USER_MANUAL + + return uri + + +def open_user_manual() -> None: + """Open the user manual in browser. + + If available the local manual is used otherwise the online version is + opened. + """ + open_url(user_manual_uri()) + + +def _show_qt_debug_info(the_qapp: QApplication): + if not logger.DEBUG: + return + try: - return parent.showHiddenFiles - except: pass - return False + info = { + # The platform name indicates eg. wayland vs. X11, see also: + # https://doc.qt.io/qt-5/qguiapplication.html#platformName-prop + # For more details see our X11/Wayland/Qt documentation in the + # directory doc/maintain + 'QT QPA platform plugin': the_qapp.platformName(), + 'QT_QPA_PLATFORMTHEME': + os.environ.get('QT_QPA_PLATFORMTHEME', ''), + # styles and themes determine the look & feel of the GUI + 'QT_STYLE_OVERRIDE': + os.environ.get('QT_STYLE_OVERRIDE', ''), + 'QT active style': the_qapp.style().objectName(), + 'QT fallback style': QIcon.fallbackThemeName(), + 'QT supported styles': QStyleFactory.keys(), + 'themeSearchPaths': QIcon.themeSearchPaths(), + 'fallbackSearchPaths': QIcon.fallbackSearchPaths(), + # The Back In Time system tray icon can only be shown if the + # desktop environment supports this + 'Is SystemTray available': + QSystemTrayIcon.isSystemTrayAvailable(), + } + + # msg = '\n' + json.dumps(info, indent=4) + msg = json.dumps(info) + + except Exception as exc: # pylint: disable=broad-exception-caught + msg = f'Error reading QT QPA platform plugin or style: {exc}' + + logger.debug(msg) + + +def create_qapplication(app_name=bitbase.APP_NAME) -> QApplication: + """Create a QAppliction instance or return the existing one. + + Dev note (buhtz, 2025-10): Refactoring is needed. e.g. A QApplication + derived class (BITApplication) to handle the singleton. + """ + + # pylint: disable-next=global-variable-undefined + global qapp # noqa: PLW0603 -def createQApplication(app_name = 'Back In Time'): - global qapp try: - return qapp + # pylint: disable-next=used-before-assignment + return qapp # "singleton pattern": Reuse already instantiated qapp except NameError: pass - if StrictVersion(QT_VERSION_STR) >= StrictVersion('5.6') and \ - hasattr(Qt, 'AA_EnableHighDpiScaling'): - QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) + qapp = QApplication(sys.argv) + + _show_qt_debug_info(qapp) + + # Release Candidate indicator + if version.IS_RELEASE_CANDIDATE: + app_name = f'{app_name} -- RELEASE CANDIDATE -- ' \ + f'({version.__version__})' + + elif version.IS_UNSTABLE_DEV_VERSION: + app_name = f'{app_name} -- UNSTABLE DEVELOPMENT ' \ + f'VERSION -- ({version.__version__})' + + # This will influence the main window title qapp.setApplicationName(app_name) - if os.geteuid() == 0 and \ - qapp.style().objectName().lower() == 'windows' and \ - 'GTK+' in QStyleFactory.keys(): - qapp.setStyle('GTK+') + + try: + + if bitbase.IS_IN_ROOT_MODE: + qapp.setApplicationName(app_name + " (root)") + qapp.setDesktopFileName("backintime-qt-root") + + else: + qapp.setDesktopFileName("backintime-qt") + + except Exception as exc: # pylint: disable=broad-exception-caught + logger.warning('Could not set App ID (required for Wayland App icon ' + f'and more). Reason: {exc}') + + # With "--debug" arg show the QT QPA platform name in the main window's + # title + if logger.DEBUG: + qapp.setApplicationName( + f'{qapp.applicationName()} ' + f'[QT QPA platform: "{qapp.platformName()}"]') + return qapp -def translator(): + +def initiate_translator(language_code: str) -> QTranslator: + """Creating an Qt related translator. + + Args: + language_code: Language code to use (based on ISO-639-1). + + This is done beside the primarily used GNU gettext because Qt need to + translate its own default elements like Yes/No-buttons. The systems + current local is used when no language code is provided. Translation is + deactivated if language code is unknown. + """ + translator = QTranslator() - locale = QLocale.system().name() - translator.load('qt_%s' % locale, - QLibraryInfo.location(QLibraryInfo.TranslationsPath)) + + if language_code: + logger.debug(f'Language code "{language_code}".') + else: + logger.debug('No language code. Use systems current locale.') + language_code = QLocale.system().name() + + rc = translator.load( + f'qt_{language_code}', + QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)) + + if rc is False: + logger.warning( + 'PyQt (the GUI library) could not install a translator for the ' + f'language code "{language_code}". Standard GUI elements will ' + 'fall back to the source language (English). This does not ' + 'affect the translation of Back In Time-specific GUI elements.') + + tools.set_locale_by_language_code(language_code) + return translator -def indexFirstColumn(idx): - if idx.column() > 0: - idx = idx.sibling(idx.row(), 0) - return idx -class MyTreeView(QTreeView): - """ - subclass QTreeView to emit a SIGNAL myCurrentIndexChanged - if the SLOT currentChanged is called - """ - myCurrentIndexChanged = pyqtSignal(QModelIndex, QModelIndex) - - def currentChanged(self, current, previous): - self.myCurrentIndexChanged.emit(current, previous) - super(MyTreeView, self).currentChanged(current, previous) - -class TimeLine(QTreeWidget): - updateFilesView = pyqtSignal(int) - - def __init__(self, parent): - super(TimeLine, self).__init__(parent) - self.setRootIsDecorated(False) - self.setEditTriggers(QAbstractItemView.NoEditTriggers) - self.setSelectionMode(QAbstractItemView.ExtendedSelection) - self.setHeaderLabels([_('Snapshots'),'foo']) - self.setSortingEnabled(True) - self.sortByColumn(1, Qt.DescendingOrder) - self.hideColumn(1) - self.header().setSectionsClickable(False) - - self.parent = parent - self.snapshots = parent.snapshots - self._resetHeaderData() - - def clear(self): - self._resetHeaderData() - return super(TimeLine, self).clear() - - def _resetHeaderData(self): - self.now = date.today() - #list of tuples with (text, startDate, endDate) - self.headerData = [] - todayMin = datetime.combine(self.now, datetime.min.time()) - todayMax = datetime.combine(self.now, datetime.max.time()) - self.headerData.append((_('Today'), todayMin, todayMax)) - - yesterdayMin = datetime.combine(self.now - timedelta(days = 1), datetime.min.time()) - yesterdayMax = datetime.combine(todayMin - timedelta(hours = 1), datetime.max.time()) - self.headerData.append((_('Yesterday'), yesterdayMin, yesterdayMax)) - - thisWeekMin = datetime.combine(self.now - timedelta(self.now.weekday()), datetime.min.time()) - thisWeekMax = datetime.combine(yesterdayMin - timedelta(hours = 1), datetime.max.time()) - if thisWeekMin < thisWeekMax: - self.headerData.append((_('This week'), thisWeekMin, thisWeekMax)) - - lastWeekMin = datetime.combine(self.now - timedelta(self.now.weekday() + 7), datetime.min.time()) - lastWeekMax = datetime.combine(self.headerData[-1][1] - timedelta(hours = 1), datetime.max.time()) - self.headerData.append((_('Last week'), lastWeekMin, lastWeekMax)) - - #the rest of current month. Otherwise this months header would be above today - thisMonthMin = datetime.combine(self.now - timedelta(self.now.day - 1), datetime.min.time()) - thisMonthMax = datetime.combine(lastWeekMin - timedelta(hours = 1), datetime.max.time()) - if thisMonthMin < thisMonthMax: - self.headerData.append((thisMonthMin.strftime('%B').capitalize(), - thisMonthMin, thisMonthMax)) - - #the rest of last month - lastMonthMax = datetime.combine(self.headerData[-1][1] - timedelta(hours = 1), datetime.max.time()) - lastMonthMin = datetime.combine(date(lastMonthMax.year, lastMonthMax.month, 1), datetime.min.time()) - self.headerData.append((lastMonthMin.strftime('%B').capitalize(), - lastMonthMin, lastMonthMax)) - - def addRoot(self, sid): - self.rootItem = self.addSnapshot(sid) - return self.rootItem - - @pyqtSlot(snapshots.SID) - def addSnapshot(self, sid): - item = SnapshotItem(sid) - - self.addTopLevelItem(item) - - #select the snapshot that was selected before - if sid == self.parent.sid: - self.setCurrentItem(item) - - if not sid.isRoot: - self.addHeader(sid) - return item - - def addHeader(self, sid): - for text, startDate, endDate in self.headerData: - if startDate <= sid.date <= endDate: - return self._createHeaderItem(text, endDate) - - #any previous months - year = sid.date.year - month = sid.date.month - if year == self.now.year: - text = date(year, month, 1).strftime('%B').capitalize() - else: - text = date(year, month, 1).strftime('%B, %Y').capitalize() - startDate = datetime.combine(date(year, month, 1), datetime.min.time()) - endDate = datetime.combine(date(year, month, monthrange(year, month)[1]), datetime.max.time()) - if self._createHeaderItem(text, endDate): - self.headerData.append((text, startDate, endDate)) - - def _createHeaderItem(self, text, endDate): - for item in self.iterHeaderItems(): - if item.snapshotID().date == endDate: - return False - item = HeaderItem(text, snapshots.SID(endDate, self.parent.config)) - self.addTopLevelItem(item) - return True - - @pyqtSlot() - def checkSelection(self): - if self.currentItem() is None: - self.selectRootItem() - - def selectRootItem(self): - self.setCurrentItem(self.rootItem) - if not self.parent.sid.isRoot: - self.parent.sid = self.rootItem.snapshotID() - self.updateFilesView.emit(2) - - def selectedSnapshotIDs(self): - return [i.snapshotID() for i in self.selectedItems()] - - def currentSnapshotID(self): - item = self.currentItem() - if item: - return item.snapshotID() - - def setCurrentSnapshotID(self, sid): - for item in self.iterItems(): - if item.snapshotID() == sid: - self.setCurrentItem(item) - break - - def setCurrentItem(self, item, *args, **kwargs): - super(TimeLine, self).setCurrentItem(item, *args, **kwargs) - if self.parent.sid != item.snapshotID(): - self.parent.sid = item.snapshotID() - self.updateFilesView.emit(2) - - def iterItems(self): - for index in range(self.topLevelItemCount()): - yield self.topLevelItem(index) - - def iterSnapshotItems(self): - for item in self.iterItems(): - if isinstance(item, SnapshotItem): - yield item - - def iterHeaderItems(self): - for item in self.iterItems(): - if isinstance(item, HeaderItem): - yield item - -class TimeLineItem(QTreeWidgetItem): - def __lt__(self, other): - return self.snapshotID() < other.snapshotID() - - def snapshotID(self): - return self.data(0, Qt.UserRole) - -class SnapshotItem(TimeLineItem): - def __init__(self, sid): - super(SnapshotItem, self).__init__() - self.setText(0, sid.displayName) - self.setFont(0, fontNormal(self.font(0))) - - self.setData(0, Qt.UserRole, sid) - - if sid.isRoot: - self.setToolTip(0, _('This is NOT a snapshot but a live view of your local files')) - else: - self.setToolTip(0, _('Last check %s') %sid.lastChecked) - - def updateText(self): - sid = self.snapshotID() - self.setText(0, sid.displayName) - -class HeaderItem(TimeLineItem): - def __init__(self, name, sid): - super(HeaderItem, self).__init__() - self.setText(0, name) - self.setFont(0, fontBold(self.font(0))) - self.setBackground(0, QColor(196, 196, 196)) - self.setForeground(0, QColor(60, 60, 60)) - self.setFlags(Qt.NoItemFlags) - - self.setData(0, Qt.UserRole, sid) - -class SortedComboBox(QComboBox): - #prevent inserting items abroad from addItem because this would break sorting - insertItem = NotImplemented - - def __init__(self, parent = None): - super(SortedComboBox, self).__init__(parent) - self.sortOrder = Qt.AscendingOrder - self.sortRole = Qt.DisplayRole - - def addItem(self, text, userData = None): - """ - QComboBox doesn't support sorting - so this little hack is used to insert - items in sorted order. - """ - if self.sortRole == Qt.UserRole: - sortObject = userData - else: - sortObject = text - l = [self.itemData(i, self.sortRole) for i in range(self.count())] - l.append(sortObject) - l.sort(reverse = self.sortOrder) - index = l.index(sortObject) - super(SortedComboBox, self).insertItem(index, text, userData) - - def checkSelection(self): - if self.currentIndex() < 0: - self.setCurrentIndex(0) - -class SnapshotCombo(SortedComboBox): - def __init__(self, parent = None): - super(SnapshotCombo, self).__init__(parent) - self.sortOrder = Qt.DescendingOrder - self.sortRole = Qt.UserRole - - def addSnapshotID(self, sid): - assert isinstance(sid, snapshots.SID), 'sid is not snapshots.SID type: {}'.format(sid) - self.addItem(sid.displayName, sid) - - def currentSnapshotID(self): - return self.itemData(self.currentIndex()) - - def setCurrentSnapshotID(self, sid): - for i in range(self.count()): - if self.itemData(i) == sid: - self.setCurrentIndex(i) - break - -class ProfileCombo(SortedComboBox): - def __init__(self, parent): - super(ProfileCombo, self).__init__(parent) - self.getName = parent.config.profileName - - def addProfileID(self, profileID): - self.addItem(self.getName(profileID), profileID) - - def currentProfileID(self): - return self.itemData(self.currentIndex()) - - def setCurrentProfileID(self, profileID): - for i in range(self.count()): - if self.itemData(i) == profileID: - self.setCurrentIndex(i) - break - -class Menu(QMenu): - """ - Subclass QMenu to add ToolTips - """ - def event(self, e): - action = self.activeAction() - if e.type() == QEvent.ToolTip and \ - action and \ - action.toolTip() != action.text(): - QToolTip.showText(e.globalPos(), - self.activeAction().toolTip()) - else: - QToolTip.hideText() - return super(Menu, self).event(e) +@contextmanager +def block_signals(widget: QWidget) -> None: + """Context manager to temporary block Qt signals""" + widget.blockSignals(True) + + try: + yield + finally: + widget.blockSignals(False) + + +@contextmanager +def block_paint_updates(widget: QWidget) -> None: + """Context manager to temporary block Qt paintng""" + widget.setUpdatesEnabled(False) + + try: + yield + finally: + widget.setUpdatesEnabled(True) diff --git a/qt/qttools_path.py b/qt/qttools_path.py new file mode 100644 index 000000000..9364c37b1 --- /dev/null +++ b/qt/qttools_path.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Helper functions extracted from qt/qttools.py file. + +Extraction happened of problems with import dependencies. The whole path +manipulation will become obsolete when migrating to state of the art Python +packaging standards. This module is a workaround and will get refactored in +the future. +""" +import sys +from pathlib import Path + + +def as_backintime_path(*path: str) -> str: + """Get path inside ``backintime`` install folder. + + Args: + *path (str): Paths that should be joined to ``backintime``. + + Returns: + str: Child path of ``backintime`` child path e.g. + ``/usr/share/backintime/common``or ``/usr/share/backintime/qt``. + """ + result = Path(__file__).parent.parent / Path(*path) + result = result.resolve() + + return str(result) + + +def register_backintime_path(*path: str): + """Find duplicate in common/tools.py + """ + path = as_backintime_path(*path) + + if path not in sys.path: + sys.path.insert(0, path) diff --git a/qt/restoreconfigdialog.py b/qt/restoreconfigdialog.py new file mode 100644 index 000000000..dd37a12e1 --- /dev/null +++ b/qt/restoreconfigdialog.py @@ -0,0 +1,529 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Taylor Raak +# SPDX-FileCopyrightText: © 2024 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""A dialog to identify and import old Back In Time configs. +""" +import os +import datetime +import getpass +import threading +import subprocess +from typing import Any, Generator +from pathlib import Path +from queue import Queue +import logger +import bitbase +from config import Config +from snapshots import SID +from PyQt6.QtGui import (QBrush, + QColor, + QFont, + QFileSystemModel, + QPalette, + QShortcut) +from PyQt6.QtWidgets import (QDialog, + QDialogButtonBox, + QGridLayout, + QHBoxLayout, + QLabel, + QLayout, + QPushButton, + QSizePolicy, + QToolButton, + QTreeView, + QVBoxLayout, + QWidget) +from PyQt6.QtCore import (Qt, + QDir, + QModelIndex, + QTimer) +import qttools +from bitwidgets import Spinner + + +# pylint: disable-next=too-many-instance-attributes +class RestoreConfigDialog(QDialog): + """ + Show a dialog that will help to restore BITs configuration. + User can select a config from previous snapshots. + + Dev note (2025-07, buhtz): Experiencing the dialog as slow or temporary + freezing is usual, because the QFileSystemModel is resource consuming and + blocking the rest of the event loop. Unfold directories in the tree and the + directories parents is very time consuming because QFileSystemModel access + the file system each time. + """ + + def __init__(self, config: Config): + super().__init__() + + self.config = config + + # pylint: disable-next=import-outside-toplevel + import icon # noqa: PLC0415 + self.setWindowIcon(icon.SETTINGS_DIALOG) + self.setWindowTitle(_('Import configuration')) + + self.setSizePolicy( + QSizePolicy.Policy.Preferred, + QSizePolicy.Policy.Expanding + ) + + main_layout = QVBoxLayout(self) + + top_layout = QVBoxLayout() + self._create_hint(top_layout, config) + self._lbl_spinner, self._spinner, self._btn_scan \ + = self._create_scan_controls(top_layout) + + self._btn_scan.clicked.connect(self.start_scanning) + + main_layout.addLayout(top_layout, 0) + self._tree_view, self._tree_model = self._create_tree() + tree_layout = QVBoxLayout() + tree_layout.addWidget(self._tree_view) + main_layout.addLayout(tree_layout, 1) + + # expand users home + self._expand_with_parents(self._index_from_path(Path.home())) + + # colors + self._color_red, self._color_green = __class__._red_and_green() + + bottom_layout = QVBoxLayout() + + # show where a snapshot with config was found + self._lbl_found = QLabel(_('No directory selected'), self) + self._lbl_found.setWordWrap(True) + self._lbl_found.setPalette(self._color_red) + bottom_layout.addWidget(self._lbl_found) + + # show profiles inside the config + self._wdg_profiles = QWidget(self) + self._wdg_profiles.setContentsMargins(0, 0, 0, 0) + self._wdg_profiles.hide() + self._grid_layout = QGridLayout() + self._grid_layout.setContentsMargins(0, 0, 0, 0) + self._grid_layout.setHorizontalSpacing(20) + self._wdg_profiles.setLayout(self._grid_layout) + bottom_layout.addWidget(self._wdg_profiles) + + self._config_to_restore = None + + self._tree_view.selectionModel().currentChanged.connect( + self._slot_index_changed) + + btn_box = QDialogButtonBox( + QDialogButtonBox.StandardButton.Ok + | QDialogButtonBox.StandardButton.Cancel, + self + ) + btn_box.accepted.connect(self.accept) + btn_box.rejected.connect(self.reject) + + self._btn_restore = btn_box.button(QDialogButtonBox.StandardButton.Ok) + self._btn_restore.setText(_('Import')) + self._btn_restore.setEnabled(False) + + bottom_layout.addWidget(btn_box) + + main_layout.addLayout(bottom_layout, 0) + + self._queue = Queue() + + self._pool_timer = QTimer(self) + self._pool_timer.timeout.connect(self._process_found_queue) + + self._scan_fs_thread = None + + self.start_scanning() + + def start_scanning(self): + """Start the file system scanning thread and prepare the GUI""" + self._btn_scan.setVisible(False) + self._pool_timer.start(1500) # milliseconds + self._lbl_spinner.setText(_('Searching…')) + self._spinner.start(interval_ms=200) + self._scan_fs_thread = _ScanFileSystem(queue=self._queue) + self._scan_fs_thread.start() + + def _create_tree(self) -> tuple[QTreeView, QFileSystemModel]: + model = _CfgFileSystemModel(self) + model.setRootPath(QDir().rootPath()) + model.setReadOnly(True) + model.setFilter(QDir.Filter.AllDirs | QDir.Filter.NoDotAndDotDot) + + view = QTreeView(self) + view.setSizePolicy( + QSizePolicy.Policy.Expanding, + QSizePolicy.Policy.Expanding + ) + + view.setModel(model) + view.setAnimated(False) + + # Hide all columns (size, typ, mod date) except the first (name) + for col in range(1, view.header().count()+1): + view.setColumnHidden(col, True) + + view.header().hide() + + return view, model + + @staticmethod + def _red_and_green() -> tuple[QColor, QColor]: + red = QPalette() + red.setColor(QPalette.ColorRole.WindowText, QColor(205, 0, 0)) + + green = QPalette() + green.setColor(QPalette.ColorRole.WindowText, QColor(0, 160, 0)) + + return red, green + + def _create_hint(self, + parent_layout: QLayout, + config: Config) -> None: + """Create the label to explain how and where to find existing config + file. + + Returns: + (QLabel): The label + """ + + sample_path = os.path.join( + 'backintime', + config.host(), + getpass.getuser(), '1', + SID(datetime.datetime.now(), config).sid + ) + sample_path = f'{sample_path}' + + text_a = _( + 'Select the backup directory from which the configuration ' + 'file should be imported. The path may look like: {samplePath}' + ).format(samplePath=sample_path) + + text_b = _( + 'If the directory is located on an external or remote drive, ' + 'it must be manually mounted beforehand.' + ) + + label = QLabel(f'

{text_a}

{text_b}

', self) + label.setWordWrap(True) + + layout = QHBoxLayout() + layout.addWidget(qttools.create_icon_label_info(icon_scale_factor=2)) + layout.addWidget(label, stretch=1) + + parent_layout.addLayout(layout) + + def _create_scan_controls(self, parent_layout: QLayout + ) -> tuple[QLabel, Spinner, QPushButton]: + # pylint: disable-next=import-outside-toplevel + import icon # noqa: PLC0415 + + lbl_spinner = QLabel(_('Searching…'), self) + spinner = Spinner(self, font_scale=2) + + btn_scan = QPushButton(_('Scan again'), self) + btn_scan.setIcon(icon.REFRESH) + + hbox = QHBoxLayout() + hbox.addWidget(lbl_spinner) + hbox.addWidget(spinner) + hbox.addWidget(btn_scan) + hbox.addStretch() + hbox.addWidget(self._create_button_show_hidden()) + + parent_layout.addLayout(hbox) + + return lbl_spinner, spinner, btn_scan + + def _create_button_show_hidden(self) -> QToolButton: + # pylint: disable-next=import-outside-toplevel + import icon # noqa: PLC0415 + + btn = QToolButton(self) + btn.setText(_('Show hidden directories')) + btn.setIcon(icon.SHOW_HIDDEN) + btn.setToolTip(_('Show/hide hidden directories (Ctrl+H)')) + btn.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon) + btn.setCheckable(True) + + shortcut = QShortcut('Ctrl+H', self) + shortcut.activated.connect(btn.toggle) + + btn.setChecked(False) + btn.toggled.connect(self._slot_show_hidden) + + return btn + + def _path_from_index(self, index: QModelIndex) -> Path: + """ + return a path string for a given treeView index + """ + return Path(self._tree_model.filePath(index)) + + def _index_from_path(self, path: str | Path) -> QModelIndex: + """ + return the index for path which can be used in treeView + """ + + idx = self._tree_model.index( + str(path) if isinstance(path, Path) else path) + + return idx + + def _slot_index_changed(self, current, _previous): + """Called every time a new item is chosen in treeView. + + If there was a config found inside the selected folder, show + available information about the config. + """ + # pylint: disable=protected-access + fp = self._path_from_index(current) + cfg = _get_valid_config(fp / bitbase.FILENAME_CONFIG) + + if cfg: + self._expand_with_parents(current) + + self._lbl_found.setText(str(fp)) + self._lbl_found.setPalette(self._color_green) + self._show_profile(cfg) + self._config_to_restore = cfg + + else: + self._lbl_found.setText(_('No config found in this directory')) + self._lbl_found.setPalette(self._color_red) + self._wdg_profiles.hide() + self._config_to_restore = None + + self._btn_restore.setEnabled(bool(cfg)) + + def _expand_with_parents(self, index: QModelIndex): + stack = [] + + # Remember index's of the entry and all its parents + current = index + while current.isValid(): + stack.insert(0, current) + current = current.parent() + + def expand_next(): + try: + self._tree_view.expand(stack.pop(0)) + # Sligthely reduce slowdown/freeze because of resource + # hungry QFileSystemModel + QTimer.singleShot(50, expand_next) + + except IndexError: + pass + + expand_next() + + def _show_profile(self, cfg): + child = self._grid_layout.takeAt(0) + + while child: + child.widget().deleteLater() + child = self._grid_layout.takeAt(0) + + for row, pid in enumerate(cfg.profiles()): + + for col, txt in enumerate(( + _('Profile:') + str(pid), + cfg.profileName(pid), + _('Mode:') + cfg.SNAPSHOT_MODES[ + cfg.snapshotsMode(pid)][1] + )): + self._grid_layout.addWidget(QLabel(txt, self), row, col) + + self._grid_layout.setColumnStretch(col, 1) + self._wdg_profiles.show() + + def _process_found_queue(self) -> None: + self._tree_view.setUpdatesEnabled(False) + + while not self._queue.empty(): + path = self._queue.get() + self._tree_model.highlight_this(Path(path)) + self._expand_with_parents(self._index_from_path(path)) + + self._tree_view.setUpdatesEnabled(True) + + # stop spinner and queue pooling if thread is empty + if not self._scan_fs_thread.is_alive(): + self._spinner.stop() + self._lbl_spinner.setText(_('Search complete.')) + self._pool_timer.stop() + self._btn_scan.setVisible(True) + + def _slot_show_hidden(self, checked): + if checked: + flags = QDir.Filter.AllDirs \ + | QDir.Filter.NoDotAndDotDot \ + | QDir.Filter.Hidden + + else: + flags = QDir.Filter.AllDirs \ + | QDir.Filter.NoDotAndDotDot \ + + self._tree_model.setFilter(flags) + + def accept(self): + """ + handle over the dict from the selected config. The dict contains + all settings from the config. + """ + if self._config_to_restore: + self.config.dict = self._config_to_restore.dict + + super().accept() + + def exec(self): + """ + stop the scan thread if it is still running after dialog was closed. + """ + ret = super().exec() + self._scan_fs_thread.stop() + + return ret + + +class _CfgFileSystemModel(QFileSystemModel): + """A sub-classed file-system model to visually highlight some of its + entries.""" + + def __init__(self, parent: QWidget): + super().__init__(parent) + self._paths = [] + + font = QFont() + font.setBold(True) + + # See data() for details + self._role_result = { + Qt.ItemDataRole.ForegroundRole: QBrush( + parent.palette().color(QPalette.ColorRole.Highlight)), + Qt.ItemDataRole.FontRole: font + } + + def highlight_this(self, path: Path) -> None: + """Remember the path to draw with different font""" + self._paths.append(path) + + # notify (redraw) the view + self.layoutChanged.emit() + + def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> Any: + """Draw an entry with bold font and highlted font color if in + `self._paths`. + """ + if role in self._role_result: + file_path = Path(self.filePath(index)) + + # Return font or brush + if file_path in self._paths: + return self._role_result[role] + + return super().data(index, role) + + +class _ScanFileSystem(threading.Thread): + """A thread scanning the file system for config files related to BIT.""" + # foundConfig = pyqtSignal(str) + + def __init__(self, queue: Queue, stop_event=None): + super().__init__() + + self._queue = queue + self._stop_event = stop_event or threading.Event() + + def run(self): + """Run several searches for config files""" + search_paths = [ + str(Path.home()), + '/media', + '/mnt', + '/', # keep root at the end! + ] + + for path_to_scan in search_paths: + # Exclude the other dirs if searching in root + if path_to_scan == search_paths[-1]: + excludes = search_paths[:-1][:] + else: + excludes = [] + + for found in self._scan(path_to_scan, excludes): + if self._stop_event.is_set(): + return + + # print(f'queue.put({found=}') + self._queue.put(found) + + def _scan(self, search_path: Path, excludes: list[str] + ) -> Generator[Path, None, None]: + """Use `find` on shell to search for `config` files.""" + + logger.debug(f'Scanning in {search_path} for config files', self) + cmd = ['find', str(search_path)] + + # exclude directories: defaults + extras + for exclude in ['/proc', '/var', '/sys', '/tmp', '/run'] + excludes: + cmd = cmd + ['(', '-path', exclude, '-prune', ')', '-o'] + + cmd = cmd + [ + '(', + '-type', + 'f', + '-name', + bitbase.FILENAME_CONFIG, + '-print', + ')' + ] + + with subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True) as proc: + + for line in proc.stdout: + + if self._stop_event.is_set(): + return + + path = Path(line.strip()) + + if _get_valid_config(path): + yield path.parent + + def stop(self): + """Prepare stop and wait for finish.""" + self._stop_event.set() + self.join() + + +def _get_valid_config(path: Path) -> Config | None: + try: + cfg = Config(str(path)) + if cfg.isConfigured(): + return cfg + + except (FileNotFoundError, UnicodeDecodeError): + pass + + # pylint: disable-next=broad-exception-caught + except Exception as exc: + logger.critical(f'Unhandled branch in code!\n{exc}\n{__file__}') + + return None diff --git a/qt/restoredialog.py b/qt/restoredialog.py index b772eeff2..908eda2f7 100644 --- a/qt/restoredialog.py +++ b/qt/restoredialog.py @@ -1,38 +1,31 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -import os -import gettext - -import tools - -from PyQt5.QtGui import * -from PyQt5.QtWidgets import * -from PyQt5.QtCore import * - -import qttools - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module offering RestoreDialog""" +from pathlib import Path +from PyQt6.QtGui import QDesktopServices +from PyQt6.QtWidgets import (QDialog, + QDialogButtonBox, + QPlainTextEdit, + QVBoxLayout) +from PyQt6.QtCore import QMutex, QThread, QTimer, QUrl +from inhibitsuspend import InhibitSuspend +import messagebox -_=gettext.gettext class RestoreDialog(QDialog): - def __init__(self, parent, sid, what, where = '', **kwargs): - super(RestoreDialog, self).__init__(parent) + """A dialog showing a live log of a restore process.""" + # pylint: disable=too-many-instance-attributes + + def __init__(self, parent, sid, what, where='', **kwargs): + super().__init__(parent) self.resize(600, 500) self.config = parent.config @@ -41,87 +34,104 @@ def __init__(self, parent, sid, what, where = '', **kwargs): self.what = what self.where = where self.kwargs = kwargs - import icon - self.logFile = self.config.restoreLogFile() - if os.path.exists(self.logFile): - os.remove(self.logFile) + # pylint: disable-next=import-outside-toplevel + import icon # noqa: PLC0415 + + self._log_file = Path(self.config.restoreLogFile()) + if self._log_file.exists(): + self._log_file.unlink() self.setWindowIcon(icon.RESTORE_DIALOG) self.setWindowTitle(_('Restore')) - self.mainLayout = QVBoxLayout(self) - - #text view - self.txtLogView = QPlainTextEdit(self) - self.txtLogView.setReadOnly(True) - self.txtLogView.setLineWrapMode(QPlainTextEdit.NoWrap) - self.txtLogView.setMaximumBlockCount(100000) - self.mainLayout.addWidget(self.txtLogView) - - #buttons - buttonBox = QDialogButtonBox(QDialogButtonBox.Close) - showLog = buttonBox.addButton(_('Show full Log'), QDialogButtonBox.ActionRole) - self.mainLayout.addWidget(buttonBox) - self.btnClose = buttonBox.button(QDialogButtonBox.Close) - self.btnClose.setEnabled(False) - buttonBox.rejected.connect(self.close) - showLog.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(self.logFile))) - - #restore in separate thread + self._main_layout = QVBoxLayout(self) + + self._txt_log_view = QPlainTextEdit(self) + self._txt_log_view.setReadOnly(True) + self._txt_log_view.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) + self._txt_log_view.setMaximumBlockCount(100000) + self._main_layout.addWidget(self._txt_log_view) + + button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) + btn_show_log = button_box.addButton( + _('Show full Log'), QDialogButtonBox.ButtonRole.ActionRole) + self._main_layout.addWidget(button_box) + self._btn_close = button_box.button( + QDialogButtonBox.StandardButton.Close) + self._btn_close.setEnabled(False) + button_box.rejected.connect(self.close) + btn_show_log.clicked.connect(self._slot_show_log) + + # restore in separate thread self.thread = RestoreThread(self) - self.thread.finished.connect(self.threadFinished) + self.thread.finished.connect(self._slot_thread_finished) + + # refresh log every 200ms + self._refesh_timer = QTimer(self) + self._refesh_timer.setInterval(200) + self._refesh_timer.setSingleShot(False) + self._refesh_timer.timeout.connect(self._slot_refresh_log) + + def _slot_show_log(self): + if not self._log_file.exists(): + messagebox.critical( + self, + f'Log file ("{self._log_file}") not found.') + return - #refresh log every 200ms - self.refreshTimer = QTimer(self) - self.refreshTimer.setInterval(200) - self.refreshTimer.setSingleShot(False) - self.refreshTimer.timeout.connect(self.refreshLog) + QDesktopServices.openUrl(QUrl(str(self._log_file))) - def refreshLog(self): + def _slot_refresh_log(self): """ get new log from thread """ - newLog = self.thread.buffer[:] - size = len(newLog) + new_log = self.thread.buffer[:] + size = len(new_log) if size: self.thread.mutex.lock() self.thread.buffer = self.thread.buffer[size:] self.thread.mutex.unlock() - self.txtLogView.appendPlainText(newLog.rstrip('\n')) + self._txt_log_view.appendPlainText(new_log.rstrip('\n')) def exec(self): - #inhibit suspend/hibernate during restore - self.config.inhibitCookie = tools.inhibitSuspend(toplevel_xid = self.config.xWindowId, reason = 'restoring') + """Show dialog and run underlying threads""" self.show() - self.refreshTimer.start() + self._refesh_timer.start() self.thread.start() - super(RestoreDialog, self).exec() - self.refreshTimer.stop() + super().exec() + self._refesh_timer.stop() self.thread.wait() - def threadFinished(self): - self.btnClose.setEnabled(True) - #release inhibit suspend - if self.config.inhibitCookie: - self.config.inhibitCookie = tools.unInhibitSuspend(*self.config.inhibitCookie) + def _slot_thread_finished(self): + self._btn_close.setEnabled(True) + class RestoreThread(QThread): """ run restore in a separate Thread to prevent GUI freeze and speed up restore """ + def __init__(self, parent): - super(RestoreThread, self).__init__() + super().__init__() self.parent = parent - self.log = open(parent.logFile, 'wt') + self.log = parent._log_file.open('wt', encoding='utf-8') self.mutex = QMutex() self.buffer = '' def run(self): - self.parent.snapshots.restore(self.parent.sid, self.parent.what, self.callback, self.parent.where, **self.parent.kwargs) + """Run the thread""" + with InhibitSuspend(reason='restoring'): + self.parent.snapshots.restore( + self.parent.sid, + self.parent.what, + self.callback, + self.parent.where, + **self.parent.kwargs) + self.log.close() - def callback(self, line, *args): + def callback(self, line, *_args): """ write into log file and provide thread save string for log window """ diff --git a/qt/serviceHelper.py b/qt/serviceHelper.py index 89c69e6c6..1a56fe608 100644 --- a/qt/serviceHelper.py +++ b/qt/serviceHelper.py @@ -1,41 +1,41 @@ -# (from BackInTime) -# Copyright (C) 2015-2021 Germar Reitze +# SPDX-FileCopyrightText: © 2004-2006 Red Hat Inc. +# SPDX-FileCopyrightText: © 2005-2007 Collabora Ltd. +# SPDX-FileCopyrightText: © 2008 Canonical Ltd. +# SPDX-FileCopyrightText: © 2009 David D. Lowe +# SPDX-FileCopyrightText: © 2015-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2025 Christian Buhtz # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This file is released under several licenses mentioned above. The file is +# part of the program "Back In Time". The program as a whole is released under +# GNU General Public License v2 (GPLv2). See LICENSES directory or go to +# - . +# - +# - # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -# (from jockey) -# (c) 2008 Canonical Ltd. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# Note about the licenses by Christian Buhtz (2024-09): +# Despite extensive research and attempts to contact the aforementioned +# individuals and institutions, it was not possible to definitively determine +# which of the mentioned licenses and copyright notices apply to which parts of +# the code contained in this file. The situation could not be clarified even +# with the git commit history. +# It should be noted that, in case of doubt, preference should be given to the +# strongest or most restrictive license. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -# (from python-dbus-docs) -# Copyright (C) 2004-2006 Red Hat Inc. -# Copyright (C) 2005-2007 Collabora Ltd. +# Before SPDX meta data was added to the file it originally had some comments +# that are summarized as follows: +# - Germar Reitze claimed GPL-2.0-or-later in context of Back In Time. +# - Unknown person claimed GPL-2.0-or-later in context of "jockey". +# - Read Hat Inc. and Collabora Ltd. claimed MIT License in context of +# "python-dbus-docs" +# - David D. Lowe claimed CC0-1.0 (public domain) in unknown context. # +# Because of MIT License the following permission notice need to be included +# in this file and should not be removed: +# --- Begin of MIT License permission notice --- # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without @@ -55,12 +55,8 @@ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -# -# This file was modified by David D. Lowe in 2009. -# To the extent possible under law, David D. Lowe has waived all -# copyright and related or neighboring rights to his modifications to -# this file under this license: http://creativecommons.org/publicdomain/zero/1.0/ - +# --- End of MIT License permission notice --- +"""Handling udev rules via DBUS service""" import os import re from subprocess import Popen, PIPE @@ -71,24 +67,54 @@ import dbus import dbus.service -import dbus.mainloop.pyqt5 -from PyQt5.QtCore import QCoreApplication +import dbus.mainloop + +# WORKAROUND +try: + # pylint: disable-next=import-error,useless-suppression + import dbus.mainloop.pyqt6 + # pylint: disable-next=import-error,useless-suppression + from dbus.mainloop.pyqt6 import DBusQtMainLoop +except (ModuleNotFoundError, ImportError) as exc: + msg = ( + 'Unable to import "dbus.mainloop.pyqt6". Original exception message: ' + f'"{exc}". Try to install package "python3-dbus.mainloop.pyqt6".' + ) + print(f'ERROR: {msg}') + import syslog + import sys + syslog.syslog(syslog.LOG_ERR, f'Back In Time: {msg}') + sys.exit(os.EX_OSERR) + + +from PyQt6.QtCore import QCoreApplication UDEV_RULES_PATH = '/etc/udev/rules.d/99-backintime-%s.rules' + class InvalidChar(dbus.DBusException): + """Exception about invalid characters""" _dbus_error_name = 'net.launchpad.backintime.InvalidChar' + class InvalidCmd(dbus.DBusException): + """EXception about an invalid command""" _dbus_error_name = 'net.launchpad.backintime.InvalidCmd' + class LimitExceeded(dbus.DBusException): + """Exception about a reached limit in several contexts""" _dbus_error_name = 'net.launchpad.backintime.LimitExceeded' + class PermissionDeniedByPolicy(dbus.DBusException): + """Exception about denied permissions""" _dbus_error_name = 'com.ubuntu.DeviceDriver.PermissionDeniedByPolicy' + class UdevRules(dbus.service.Object): + """DBus object to manage Udev rules""" + def __init__(self, conn=None, object_path=None, bus_name=None): super(UdevRules, self).__init__(conn, object_path, bus_name) @@ -98,62 +124,100 @@ def __init__(self, conn=None, object_path=None, bus_name=None): self.tmpDict = {} - #find su path self.su = self._which('su', '/bin/su') self.backintime = self._which('backintime', '/usr/bin/backintime') - self.nice = self._which('nice', '/usr/bin/nice') - self.ionice = self._which('ionice', '/usr/bin/ionice') + self.max_rules = 100 self.max_users = 20 - self.max_cmd_len = 100 + self.max_cmd_len = 120 # was 100 before but was too small (see #1027) def _which(self, exe, fallback): - proc = Popen(['which', exe], stdout = PIPE) - ret = proc.communicate()[0].strip().decode() + proc = Popen(['which', exe], stdout=PIPE) + + ret = proc.communicate() + # ret = proc.communicate()[0].strip().decode() + ret = ret[0].strip().decode() + if proc.returncode or not ret: return fallback return ret def _validateCmd(self, cmd): + """ ??? + """ if cmd.find("&&") != -1: raise InvalidCmd("Parameter 'cmd' contains '&&' concatenation") - # make sure it starts with an absolute path - elif not cmd.startswith(os.path.sep): - raise InvalidCmd("Parameter 'cmd' does not start with '/'") - parts = cmd.split() + # make sure it starts with an absolute path + if not cmd.startswith(os.path.sep): + raise InvalidCmd( + f'Parameter "cmd" does not start with "{os.path.sep}"') # make sure only well known commands and switches are used whitelist = ( - (self.nice, r'^-n'), - (self.ionice, r'(^-c|^-n)'), + ( + # command itself + self._which('nice', '/usr/bin/nice'), + # command options/switches beginning with "-n" + r'^-n' + ), + ( + # command itself + self._which('ionice', '/usr/bin/ionice'), + # command options/switches beginning with "-c" or "-n" + r'(^-c|^-n)' + ), ) + parts = cmd.split() + + # Remove whitelisted commands and their options/switches while parts: + for c, switches in whitelist: + + # whitelist command? if parts[0] == c: + + # remove from parts list parts.pop(0) + + # every whitelisted option/switch while parts and re.match(switches, parts[0]): + # remove from parts list parts.pop(0) + break + else: break + # See what's left in parts if not parts: - raise InvalidCmd("Parameter 'cmd' does not contain the backintime command") - elif parts[0] != self.backintime: - raise InvalidCmd("Parameter 'cmd' contains non-whitelisted cmd/parameter (%s)" % parts[0]) + msg = "Parameter 'cmd' does not contain the backintime command" + msg = f'{msg}\n{cmd=}\nrest of {parts=}' + + raise InvalidCmd(msg) + + if parts[0] != self.backintime: + msg = "Parameter 'cmd' contains non-whitelisted cmd/parameter " \ + f"({parts[0]})" + msg = f'{msg}\n{cmd=}\nrest of {parts=}' + + raise InvalidCmd(msg) def _checkLimits(self, owner, cmd): if len(self.tmpDict.get(owner, [])) >= self.max_rules: raise LimitExceeded("Maximum number of cached rules reached (%d)" % self.max_rules) + elif len(self.tmpDict) >= self.max_users: raise LimitExceeded("Maximum number of cached users reached (%d)" % self.max_users) + elif len(cmd) > self.max_cmd_len: raise LimitExceeded("Maximum length of command line reached (%d)" % self.max_cmd_len) @@ -162,21 +226,24 @@ def _checkLimits(self, owner, cmd): in_signature='ss', out_signature='', sender_keyword='sender', connection_keyword='conn') def addRule(self, cmd, uuid, sender=None, conn=None): - """ - Receive command and uuid and create an Udev rule out of this. + """Receive command and uuid and create an Udev rule out of this. + This is done on the service side to prevent malicious code to run as root. """ - #prevent breaking out of su command + # prevent breaking out of su command chars = re.findall(r'[^a-zA-Z0-9-/\.>& ]', cmd) if chars: - raise InvalidChar("Parameter 'cmd' contains invalid character(s) %s" - % '|'.join(set(chars))) - #only allow relevant chars in uuid + raise InvalidChar( + "Parameter 'cmd' contains invalid character(s) %s" + % '|'.join(set(chars))) + + # only allow relevant chars in uuid chars = re.findall(r'[^a-zA-Z0-9-]', uuid) if chars: - raise InvalidChar("Parameter 'uuid' contains invalid character(s) %s" - % '|'.join(set(chars))) + raise InvalidChar( + "Parameter 'uuid' contains invalid character(s) %s" + % '|'.join(set(chars))) self._validateCmd(cmd) @@ -186,70 +253,86 @@ def addRule(self, cmd, uuid, sender=None, conn=None): self._checkLimits(owner, cmd) - #create su command - sucmd = "%s - '%s' -c '%s'" %(self.su, user, cmd) - #create Udev rule - rule = 'ACTION=="add|change", ENV{ID_FS_UUID}=="%s", RUN+="%s"\n' %(uuid, sucmd) + # create su command + sucmd = f"{self.su} - '{user}' -c '{cmd}'" - #store rule + # create Udev rule + rule = 'ACTION=="add|change", ENV{ID_FS_UUID}=="' \ + + uuid \ + + '", RUN+="' \ + + sucmd \ + + '"\n' + + print(f'{sucmd=} {rule=}') + + # store rule if not owner in self.tmpDict: self.tmpDict[owner] = [] + self.tmpDict[owner].append(rule) @dbus.service.method("net.launchpad.backintime.serviceHelper.UdevRules", in_signature='', out_signature='b', sender_keyword='sender', connection_keyword='conn') def save(self, sender=None, conn=None): - """ - Save rules to destiantion file after user authenticated as admin. + """Save rules to destination file after user authenticated as admin. + This will first check if there are any changes between - temporary added rules and current rules in destiantion file. + temporary added rules and current rules in destination file. Returns False if files are identical or no rules to be installed. """ info = SenderInfo(sender, conn) user = info.connectionUnixUser() owner = info.nameOwner() - #delete rule if no rules in tmp + # delete rule if no rules in tmp if not owner in self.tmpDict or not self.tmpDict[owner]: self.delete(sender, conn) + return False - #return False if rule already exist. + + # return False if rule already exist. if os.path.exists(UDEV_RULES_PATH % user): + with open(UDEV_RULES_PATH % user, 'r') as f: + if self.tmpDict[owner] == f.readlines(): self._clean(owner) + return False - #auth to save changes - self._checkPolkitPrivilege(sender, conn, 'net.launchpad.backintime.UdevRuleSave') + + # auth to save changes + self._checkPolkitPrivilege( + sender, conn, 'net.launchpad.backintime.UdevRuleSave') + with open(UDEV_RULES_PATH % user, 'w') as f: f.writelines(self.tmpDict[owner]) + self._clean(owner) + return True @dbus.service.method("net.launchpad.backintime.serviceHelper.UdevRules", in_signature='', out_signature='', sender_keyword='sender', connection_keyword='conn') def delete(self, sender=None, conn=None): - """ - Delete existing Udev rule - """ + """Delete existing Udev rule""" info = SenderInfo(sender, conn) user = info.connectionUnixUser() owner = info.nameOwner() self._clean(owner) + if os.path.exists(UDEV_RULES_PATH % user): - #auth to delete rule - self._checkPolkitPrivilege(sender, conn, 'net.launchpad.backintime.UdevRuleDelete') + # auth to delete rule + self._checkPolkitPrivilege( + sender, conn, 'net.launchpad.backintime.UdevRuleDelete') os.remove(UDEV_RULES_PATH % user) @dbus.service.method("net.launchpad.backintime.serviceHelper.UdevRules", in_signature='', out_signature='', sender_keyword='sender', connection_keyword='conn') def clean(self, sender=None, conn=None): - """ - clean up previous cached rules - """ + """clean up previous cached rules""" info = SenderInfo(sender, conn) self._clean(info.nameOwner()) @@ -287,34 +370,57 @@ def _checkPolkitPrivilege(self, sender, conn, privilege): # query PolicyKit self._initPolkit() + try: - # we don't need is_challenge return here, since we call with AllowUserInteraction - (is_auth, _, details) = self.polkit.CheckAuthorization( - ('system-bus-name', {'name': dbus.String(sender, variant_level=1)}), - privilege, {'': ''}, dbus.UInt32(1), '', timeout=3000) - except dbus.DBusException as e: - if e._dbus_error_name == 'org.freedesktop.DBus.Error.ServiceUnknown': + # We don't need is_challenge return here, since we call + # with AllowUserInteraction + (is_auth, _unknown, _details) = self.polkit.CheckAuthorization( + ( + 'system-bus-name', + {'name': dbus.String(sender, variant_level=1)} + ), + privilege, + {'': ''}, + dbus.UInt32(1), + '', + timeout=3000 + ) + + except dbus.DBusException as exc: + err_name = exc._dbus_error_name + if err_name == 'org.freedesktop.DBus.Error.ServiceUnknown': # polkitd timed out, connect again self.polkit = None + return self._checkPolkitPrivilege(sender, conn, privilege) + else: raise if not is_auth: raise PermissionDeniedByPolicy(privilege) -class SenderInfo(object): + +class SenderInfo: + """Data structure containing info about a sender using DBus""" + def __init__(self, sender, conn): self.sender = sender - self.dbus_info = dbus.Interface(conn.get_object('org.freedesktop.DBus', - '/org/freedesktop/DBus/Bus', False), 'org.freedesktop.DBus') + self.dbus_info = dbus.Interface( + conn.get_object( + 'org.freedesktop.DBus', + '/org/freedesktop/DBus/Bus', + False), + 'org.freedesktop.DBus' + ) def connectionUnixUser(self): uid = self.dbus_info.GetConnectionUnixUser(self.sender) + if pwd: return pwd.getpwuid(uid).pw_name - else: - return uid + + return uid def nameOwner(self): return self.dbus_info.GetNameOwner(self.sender) @@ -322,8 +428,9 @@ def nameOwner(self): def connectionPid(self): return self.dbus_info.GetConnectionUnixProcessID(self.sender) + if __name__ == '__main__': - dbus.mainloop.pyqt5.DBusQtMainLoop(set_as_default=True) + DBusQtMainLoop(set_as_default=True) app = QCoreApplication([]) @@ -331,5 +438,4 @@ def connectionPid(self): name = dbus.service.BusName("net.launchpad.backintime.serviceHelper", bus) object = UdevRules(bus, '/UdevRules') - print("Running BIT service.") - app.exec_() + app.exec() diff --git a/qt/settingsdialog.py b/qt/settingsdialog.py deleted file mode 100644 index 99025a3b8..000000000 --- a/qt/settingsdialog.py +++ /dev/null @@ -1,2121 +0,0 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze, Taylor Raack -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -import os -import datetime -import gettext -import copy -import grp -import re - -from PyQt5.QtGui import * -from PyQt5.QtWidgets import * -from PyQt5.QtCore import * - -import config -import tools -import qttools -import mount -import messagebox -import snapshots -import sshtools -import logger -from exceptions import MountException, NoPubKeyLogin, KnownHost - -_=gettext.gettext - - -class SettingsDialog(QDialog): - def __init__(self, parent): - super(SettingsDialog, self).__init__(parent) - - self.parent = parent - self.config = parent.config - self.snapshots = parent.snapshots - self.configDictCopy = copy.copy(self.config.dict) - self.originalCurrentProfile = self.config.currentProfile() - import icon - self.icon = icon - - self.config.setQuestionHandler(self.questionHandler) - self.config.setErrorHandler(self.errorHandler) - - self.setWindowIcon(icon.SETTINGS_DIALOG) - self.setWindowTitle(_('Settings')) - - self.mainLayout = QVBoxLayout(self) - - #profiles - layout = QHBoxLayout() - self.mainLayout.addLayout(layout) - - layout.addWidget(QLabel(_('Profile:'), self)) - - self.firstUpdateAll = True - self.disableProfileChanged = True - self.comboProfiles = qttools.ProfileCombo(self) - layout.addWidget(self.comboProfiles, 1) - self.comboProfiles.currentIndexChanged.connect(self.profileChanged) - self.disableProfileChanged = False - - self.btnEditProfile = QPushButton(icon.PROFILE_EDIT, _('Edit'), self) - self.btnEditProfile.clicked.connect(self.editProfile) - layout.addWidget(self.btnEditProfile) - - # update to full system backup button - self.btnModifyProfileForFullSystemBackup = QPushButton(icon.ADD, _('Modify for Full System Backup'), self) - self.btnModifyProfileForFullSystemBackup.clicked.connect(self.modifyProfileForFullSystemBackup) - layout.addWidget(self.btnModifyProfileForFullSystemBackup) - # hide 'full system backup button' until all dev regarding this is done - self.btnModifyProfileForFullSystemBackup.hide() - - self.btnAddProfile = QPushButton(icon.ADD, _('Add'), self) - self.btnAddProfile.clicked.connect(self.addProfile) - layout.addWidget(self.btnAddProfile) - - self.btnRemoveProfile = QPushButton(icon.REMOVE, _('Remove'), self) - self.btnRemoveProfile.clicked.connect(self.removeProfile) - layout.addWidget(self.btnRemoveProfile) - - #TABs - self.tabs = QTabWidget(self) - self.mainLayout.addWidget(self.tabs) - - #occupy whole space for tabs - scrollButtonDefault = self.tabs.usesScrollButtons() - self.tabs.setUsesScrollButtons(False) - - #TAB: General - scrollArea = QScrollArea(self) - scrollArea.setFrameStyle(QFrame.NoFrame) - self.tabs.addTab(scrollArea, _('General')) - - layoutWidget = QWidget(self) - layout = QVBoxLayout(layoutWidget) - - #select mode - self.mode = None - vlayout = QVBoxLayout() - layout.addLayout(vlayout) - - self.lblModes = QLabel(_('Mode:'), self) - - self.comboModes = QComboBox(self) - hlayout = QHBoxLayout() - hlayout.addWidget(self.lblModes) - hlayout.addWidget(self.comboModes, 1) - vlayout.addLayout(hlayout) - store_modes = {} - for key in list(self.config.SNAPSHOT_MODES.keys()): - store_modes[key] = self.config.SNAPSHOT_MODES[key][1] - self.fillCombo(self.comboModes, store_modes) - - #encfs security warning - self.encfsWarning = QLabel(_("Warning: %(app)s uses EncFS for encryption. A recent security audit " - "revealed several possible attack vectors for this. " - "Please take a look at 'A NOTE ON SECURITY' in 'man backintime'.") \ - % {'app': self.config.APP_NAME}) - self.encfsWarning.setWordWrap(True) - layout.addWidget(self.encfsWarning) - - #Where to save snapshots - groupBox = QGroupBox(self) - self.modeLocal = groupBox - groupBox.setTitle(_('Where to save snapshots')) - layout.addWidget(groupBox) - - vlayout = QVBoxLayout(groupBox) - - hlayout = QHBoxLayout() - vlayout.addLayout(hlayout) - - self.editSnapshotsPath = QLineEdit(self) - self.editSnapshotsPath.setReadOnly(True) - self.editSnapshotsPath.textChanged.connect(self.fullPathChanged) - hlayout.addWidget(self.editSnapshotsPath) - - self.btnSnapshotsPath = QToolButton(self) - self.btnSnapshotsPath.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.btnSnapshotsPath.setIcon(icon.FOLDER) - self.btnSnapshotsPath.setText(_('Folder')) - self.btnSnapshotsPath.setMinimumSize(32,28) - hlayout.addWidget(self.btnSnapshotsPath) - self.btnSnapshotsPath.clicked.connect(self.btnSnapshotsPathClicked) - - #SSH - groupBox = QGroupBox(self) - self.modeSsh = groupBox - groupBox.setTitle(_('SSH Settings')) - layout.addWidget(groupBox) - - vlayout = QVBoxLayout(groupBox) - - hlayout1 = QHBoxLayout() - vlayout.addLayout(hlayout1) - hlayout2 = QHBoxLayout() - vlayout.addLayout(hlayout2) - hlayout3 = QHBoxLayout() - vlayout.addLayout(hlayout3) - - self.lblSshHost = QLabel(_('Host:'), self) - hlayout1.addWidget(self.lblSshHost) - self.txtSshHost = QLineEdit(self) - hlayout1.addWidget(self.txtSshHost) - - self.lblSshPort = QLabel(_('Port:'), self) - hlayout1.addWidget(self.lblSshPort) - self.txtSshPort = QLineEdit(self) - hlayout1.addWidget(self.txtSshPort) - - self.lblSshUser = QLabel(_('User:'), self) - hlayout1.addWidget(self.lblSshUser) - self.txtSshUser = QLineEdit(self) - hlayout1.addWidget(self.txtSshUser) - - self.lblSshPath = QLabel(_('Path:'), self) - hlayout2.addWidget(self.lblSshPath) - self.txtSshPath = QLineEdit(self) - self.txtSshPath.textChanged.connect(self.fullPathChanged) - hlayout2.addWidget(self.txtSshPath) - - self.lblSshCipher = QLabel(_('Cipher:'), self) - hlayout3.addWidget(self.lblSshCipher) - self.comboSshCipher = QComboBox(self) - hlayout3.addWidget(self.comboSshCipher) - self.fillCombo(self.comboSshCipher, self.config.SSH_CIPHERS) - - self.lblSshPrivateKeyFile = QLabel(_('Private Key:'), self) - hlayout3.addWidget(self.lblSshPrivateKeyFile) - self.txtSshPrivateKeyFile = QLineEdit(self) - self.txtSshPrivateKeyFile.setReadOnly(True) - hlayout3.addWidget(self.txtSshPrivateKeyFile) - - self.btnSshPrivateKeyFile = QToolButton(self) - self.btnSshPrivateKeyFile.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.btnSshPrivateKeyFile.setIcon(icon.FOLDER) - self.btnSshPrivateKeyFile.setToolTip(_('Key File')) - self.btnSshPrivateKeyFile.setMinimumSize(32,28) - hlayout3.addWidget(self.btnSshPrivateKeyFile) - self.btnSshPrivateKeyFile.clicked.connect(self.btnSshPrivateKeyFileClicked) - - self.btnSshKeyGen = QToolButton(self) - self.btnSshKeyGen.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.btnSshKeyGen.setIcon(icon.ADD) - self.btnSshKeyGen.setToolTip(_('Create a new SSH key without Password.')) - self.btnSshKeyGen.setMinimumSize(32,28) - hlayout3.addWidget(self.btnSshKeyGen) - self.btnSshKeyGen.clicked.connect(self.btnSshKeyGenClicked) - self.txtSshPrivateKeyFile.textChanged.connect(lambda x: self.btnSshKeyGen.setEnabled(not x)) - - qttools.equalIndent(self.lblSshHost, self.lblSshPath, self.lblSshCipher) - - #encfs - self.modeLocalEncfs = self.modeLocal - self.modeSshEncfs = self.modeSsh - - #password - groupBox = QGroupBox(self) - self.groupPassword1 = groupBox - groupBox.setTitle(_('Password')) - layout.addWidget(groupBox) - - vlayout = QVBoxLayout(groupBox) - hlayout1 = QHBoxLayout() - vlayout.addLayout(hlayout1) - hlayout2 = QHBoxLayout() - vlayout.addLayout(hlayout2) - - self.lblPassword1 = QLabel(_('Password'), self) - hlayout1.addWidget(self.lblPassword1) - self.txtPassword1 = QLineEdit(self) - self.txtPassword1.setEchoMode(QLineEdit.Password) - hlayout1.addWidget(self.txtPassword1) - - self.lblPassword2 = QLabel(_('Password'), self) - hlayout2.addWidget(self.lblPassword2) - self.txtPassword2 = QLineEdit(self) - self.txtPassword2.setEchoMode(QLineEdit.Password) - hlayout2.addWidget(self.txtPassword2) - - self.cbPasswordSave = QCheckBox(_('Save Password to Keyring'), self) - vlayout.addWidget(self.cbPasswordSave) - - self.cbPasswordUseCache = QCheckBox(_('Cache Password for Cron (Security issue: root can read password)'), self) - vlayout.addWidget(self.cbPasswordUseCache) - - self.keyringSupported = tools.keyringSupported() - self.cbPasswordSave.setEnabled(self.keyringSupported) - - #mode change - self.comboModes.currentIndexChanged.connect(self.comboModesChanged) - - #host, user, profile id - groupBox = QGroupBox(self) - self.frameAdvanced = groupBox - groupBox.setTitle(_('Advanced')) - layout.addWidget(groupBox) - - hlayout = QHBoxLayout(groupBox) - hlayout.addSpacing(12) - - vlayout2 = QVBoxLayout() - hlayout.addLayout(vlayout2) - - hlayout2 = QHBoxLayout() - vlayout2.addLayout(hlayout2) - - self.lblHost = QLabel(_('Host:'), self) - hlayout2.addWidget(self.lblHost) - self.txtHost = QLineEdit(self) - self.txtHost.textChanged.connect(self.fullPathChanged) - hlayout2.addWidget(self.txtHost) - - self.lblUser = QLabel(_('User:'), self) - hlayout2.addWidget(self.lblUser) - self.txtUser = QLineEdit(self) - self.txtUser.textChanged.connect(self.fullPathChanged) - hlayout2.addWidget(self.txtUser) - - self.lblProfile = QLabel(_('Profile:'), self) - hlayout2.addWidget(self.lblProfile) - self.txt_profile = QLineEdit(self) - self.txt_profile.textChanged.connect(self.fullPathChanged) - hlayout2.addWidget(self.txt_profile) - - self.lblFullPath = QLabel(_('Full snapshot path: '), self) - self.lblFullPath.setWordWrap(True) - vlayout2.addWidget(self.lblFullPath) - - #Schedule - groupBox = QGroupBox(self) - self.globalScheduleGroupBox = groupBox - groupBox.setTitle(_('Schedule')) - layout.addWidget(groupBox) - - glayout = QGridLayout(groupBox) - glayout.setColumnStretch(1, 2) - - self.comboSchedule = QComboBox(self) - glayout.addWidget(self.comboSchedule, 0, 0, 1, 2) - self.fillCombo(self.comboSchedule, self.config.SCHEDULE_MODES) - - self.lblScheduleDay = QLabel(_('Day:'), self) - self.lblScheduleDay.setContentsMargins(5, 0, 0, 0) - self.lblScheduleDay.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - glayout.addWidget(self.lblScheduleDay, 1, 0) - - self.comboScheduleDay = QComboBox(self) - glayout.addWidget(self.comboScheduleDay, 1, 1) - - for d in range(1, 29): - self.comboScheduleDay.addItem(QIcon(), str(d), d) - - self.lblScheduleWeekday = QLabel(_('Weekday:'), self) - self.lblScheduleWeekday.setContentsMargins(5, 0, 0, 0) - self.lblScheduleWeekday.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - glayout.addWidget(self.lblScheduleWeekday, 2, 0) - - self.comboScheduleWeekday = QComboBox(self) - glayout.addWidget(self.comboScheduleWeekday, 2, 1) - - for d in range(1, 8): - self.comboScheduleWeekday.addItem(QIcon(), datetime.date(2011, 11, 6 + d).strftime("%A"), d) - - self.lblScheduleTime = QLabel(_('Hour:'), self) - self.lblScheduleTime.setContentsMargins(5, 0, 0, 0) - self.lblScheduleTime.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - glayout.addWidget(self.lblScheduleTime, 3, 0) - - self.comboScheduleTime = QComboBox(self) - glayout.addWidget(self.comboScheduleTime, 3, 1) - - for t in range(0, 2400, 100): - self.comboScheduleTime.addItem(QIcon(), datetime.time(t//100, t%100).strftime("%H:%M"), t) - - self.lblScheduleCronPatern = QLabel(_('Hours:'), self) - self.lblScheduleCronPatern.setContentsMargins(5, 0, 0, 0) - self.lblScheduleCronPatern.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - glayout.addWidget(self.lblScheduleCronPatern, 4, 0) - - self.txtScheduleCronPatern = QLineEdit(self) - glayout.addWidget(self.txtScheduleCronPatern, 4, 1) - - #anacron - self.lblScheduleRepeated = QLabel(_('Run Back In Time repeatedly. This is useful if the computer is not running regularly.')) - self.lblScheduleRepeated.setContentsMargins(5, 0, 0, 0) - self.lblScheduleRepeated.setWordWrap(True) - glayout.addWidget(self.lblScheduleRepeated, 5, 0, 1, 2) - - self.lblScheduleRepeatedPeriod = QLabel(_('Every:')) - self.lblScheduleRepeatedPeriod.setContentsMargins(5, 0, 0, 0) - self.lblScheduleRepeatedPeriod.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - glayout.addWidget(self.lblScheduleRepeatedPeriod, 7, 0) - - hlayout = QHBoxLayout() - self.spbScheduleRepeatedPeriod = QSpinBox(self) - self.spbScheduleRepeatedPeriod.setSingleStep(1) - self.spbScheduleRepeatedPeriod.setRange(1, 10000) - hlayout.addWidget(self.spbScheduleRepeatedPeriod) - - self.comboScheduleRepeatedUnit = QComboBox(self) - self.fillCombo(self.comboScheduleRepeatedUnit, self.config.REPEATEDLY_UNITS) - hlayout.addWidget(self.comboScheduleRepeatedUnit) - hlayout.addStretch() - glayout.addLayout(hlayout, 7, 1) - - #udev - self.lblScheduleUdev = QLabel(_('Run Back In Time as soon as the drive is connected (only once every X days).\nYou will be prompted for your sudo password.')) - self.lblScheduleUdev.setWordWrap(True) - glayout.addWidget(self.lblScheduleUdev, 6, 0, 1, 2) - - self.comboSchedule.currentIndexChanged.connect(self.scheduleChanged) - - # - layout.addStretch() - scrollArea.setWidget(layoutWidget) - scrollArea.setWidgetResizable(True) - - #TAB: Include - tabWidget = QWidget(self) - self.tabs.addTab(tabWidget, _('Include')) - layout = QVBoxLayout(tabWidget) - - self.listInclude = QTreeWidget(self) - self.listInclude.setSelectionMode(QAbstractItemView.ExtendedSelection) - self.listInclude.setRootIsDecorated(False) - self.listInclude.setHeaderLabels([ _('Include files and folders'), - 'Count' ]) - - self.listInclude.header().setSectionResizeMode(0, QHeaderView.Stretch) - self.listInclude.header().setSectionsClickable(True) - self.listInclude.header().setSortIndicatorShown(True) - self.listInclude.header().setSectionHidden(1, True) - self.listIncludeSortLoop = False - self.listInclude.header().sortIndicatorChanged.connect(self.includeCustomSortOrder) - - layout.addWidget(self.listInclude) - self.listIncludeCount = 0 - - buttonsLayout = QHBoxLayout() - layout.addLayout(buttonsLayout) - - self.btnIncludeFile = QPushButton(icon.ADD, _('Add file'), self) - buttonsLayout.addWidget(self.btnIncludeFile) - self.btnIncludeFile.clicked.connect(self.btnIncludeFileClicked) - - self.btnIncludeAdd = QPushButton(icon.ADD, _('Add folder'), self) - buttonsLayout.addWidget(self.btnIncludeAdd) - self.btnIncludeAdd.clicked.connect(self.btnIncludeAddClicked) - - self.btnIncludeRemove = QPushButton(icon.REMOVE, _('Remove'), self) - buttonsLayout.addWidget(self.btnIncludeRemove) - self.btnIncludeRemove.clicked.connect(self.btnIncludeRemoveClicked) - - #TAB: Exclude - tabWidget = QWidget(self) - self.tabs.addTab(tabWidget, _('Exclude')) - layout = QVBoxLayout(tabWidget) - - self.lblSshEncfsExcludeWarning = QLabel(_('Warning: Wildcards (\'foo*\', \'[fF]oo\', \'fo?\') will be ignored with mode \'SSH encrypted\'.\nOnly separate asterisk are allowed (\'foo/*\', \'foo/**/bar\')'), self) - self.lblSshEncfsExcludeWarning.setWordWrap(True) - layout.addWidget(self.lblSshEncfsExcludeWarning) - - self.listExclude = QTreeWidget(self) - self.listExclude.setSelectionMode(QAbstractItemView.ExtendedSelection) - self.listExclude.setRootIsDecorated(False) - self.listExclude.setHeaderLabels([ _('Exclude patterns, files or folders') , - 'Count' ]) - - self.listExclude.header().setSectionResizeMode(0, QHeaderView.Stretch) - self.listExclude.header().setSectionsClickable(True) - self.listExclude.header().setSortIndicatorShown(True) - self.listExclude.header().setSectionHidden(1, True) - self.listExcludeSortLoop = False - self.listExclude.header().sortIndicatorChanged.connect(self.excludeCustomSortOrder) - - layout.addWidget(self.listExclude) - self.listExcludeCount = 0 - - label = QLabel(_('Highly recommended:'), self) - qttools.setFontBold(label) - layout.addWidget(label) - label = QLabel(', '.join(sorted(self.config.DEFAULT_EXCLUDE)), self) - label.setWordWrap(True) - layout.addWidget(label) - - buttonsLayout = QHBoxLayout() - layout.addLayout(buttonsLayout) - - self.btnExcludeAdd = QPushButton(icon.ADD, _('Add'), self) - buttonsLayout.addWidget(self.btnExcludeAdd) - self.btnExcludeAdd.clicked.connect(self.btnExcludeAddClicked) - - self.btnExcludeFile = QPushButton(icon.ADD, _('Add file'), self) - buttonsLayout.addWidget(self.btnExcludeFile) - self.btnExcludeFile.clicked.connect(self.btnExcludeFileClicked) - - self.btnExcludeFolder = QPushButton(icon.ADD, _('Add folder'), self) - buttonsLayout.addWidget(self.btnExcludeFolder) - self.btnExcludeFolder.clicked.connect(self.btnExcludeFolderClicked) - - self.btnExcludeDefault = QPushButton(icon.DEFAULT_EXCLUDE, _('Add default'), self) - buttonsLayout.addWidget(self.btnExcludeDefault) - self.btnExcludeDefault.clicked.connect(self.btnExcludeDefaultClicked) - - self.btnExcludeRemove = QPushButton(icon.REMOVE, _('Remove'), self) - buttonsLayout.addWidget(self.btnExcludeRemove) - self.btnExcludeRemove.clicked.connect(self.btnExcludeRemoveClicked) - - #exclude files by size - hlayout = QHBoxLayout() - layout.addLayout(hlayout) - self.cbExcludeBySize = QCheckBox(_('Exclude files bigger than: '), self) - self.cbExcludeBySize.setToolTip(_('Exclude files bigger than value in %(prefix)s.\n' +\ - 'With \'Full rsync mode\' disabled this will only affect new files\n' +\ - 'because for rsync this is a transfer option, not an exclude option.\n' +\ - 'So big files that has been backed up before will remain in snapshots\n' +\ - 'even if they had changed.' %{'prefix': 'MiB'})) - hlayout.addWidget(self.cbExcludeBySize) - self.spbExcludeBySize = QSpinBox(self) - self.spbExcludeBySize.setSuffix(' MiB') - self.spbExcludeBySize.setRange(0, 100000000) - hlayout.addWidget(self.spbExcludeBySize) - hlayout.addStretch() - enabled = lambda state: self.spbExcludeBySize.setEnabled(state) - enabled(False) - self.cbExcludeBySize.stateChanged.connect(enabled) - - #TAB: Auto-remove - scrollArea = QScrollArea(self) - scrollArea.setFrameStyle(QFrame.NoFrame) - self.tabs.addTab(scrollArea, _('Auto-remove')) - - layoutWidget = QWidget(self) - layout = QGridLayout(layoutWidget) - - - #remove old snapshots - self.cbRemoveOlder = QCheckBox(_('Older than:'), self) - layout.addWidget(self.cbRemoveOlder, 0, 0) - self.cbRemoveOlder.stateChanged.connect(self.updateRemoveOlder) - - self.spbRemoveOlder = QSpinBox(self) - self.spbRemoveOlder.setRange(1, 1000) - layout.addWidget(self.spbRemoveOlder, 0, 1) - - self.comboRemoveOlderUnit = QComboBox(self) - layout.addWidget(self.comboRemoveOlderUnit, 0, 2) - self.fillCombo(self.comboRemoveOlderUnit, self.config.REMOVE_OLD_BACKUP_UNITS) - - #min free space - enabled, value, unit = self.config.minFreeSpace() - - self.cbFreeSpace = QCheckBox(_('If free space is less than:'), self) - layout.addWidget(self.cbFreeSpace, 1, 0) - self.cbFreeSpace.stateChanged.connect(self.updateFreeSpace) - - self.spbFreeSpace = QSpinBox(self) - self.spbFreeSpace.setRange(1, 1000) - layout.addWidget(self.spbFreeSpace, 1, 1) - - self.comboFreeSpaceUnit = QComboBox(self) - layout.addWidget(self.comboFreeSpaceUnit, 1, 2) - self.fillCombo(self.comboFreeSpaceUnit, self.config.MIN_FREE_SPACE_UNITS) - - #min free inodes - self.cbFreeInodes = QCheckBox(_('If free inodes is less than:'), self) - layout.addWidget(self.cbFreeInodes, 2, 0) - - self.spbFreeInodes = QSpinBox(self) - self.spbFreeInodes.setSuffix(' %') - self.spbFreeInodes.setSingleStep(1) - self.spbFreeInodes.setRange(0, 15) - layout.addWidget(self.spbFreeInodes, 2, 1) - - enabled = lambda state: self.spbFreeInodes.setEnabled(state) - enabled(False) - self.cbFreeInodes.stateChanged.connect(enabled) - - #smart remove - self.cbSmartRemove = QCheckBox(_('Smart remove'), self) - layout.addWidget(self.cbSmartRemove, 3, 0) - - widget = QWidget(self) - widget.setContentsMargins(25, 0, 0, 0) - layout.addWidget(widget, 4, 0, 1, 3) - - smlayout = QGridLayout(widget) - - self.cbSmartRemoveRunRemoteInBackground = QCheckBox(_('Run in background on remote Host.') + _(' EXPERIMENTAL!'), self) - smlayout.addWidget(self.cbSmartRemoveRunRemoteInBackground, 0, 0, 1, 3) - - smlayout.addWidget(QLabel(_('Keep all snapshots for the last'), self), 1, 0) - self.spbKeepAll = QSpinBox(self) - self.spbKeepAll.setRange(1, 10000) - smlayout.addWidget(self.spbKeepAll, 1, 1) - smlayout.addWidget(QLabel(_('day(s)'), self), 1, 2) - - smlayout.addWidget(QLabel(_('Keep one snapshot per day for the last'), self), 2, 0) - self.spbKeepOnePerDay = QSpinBox(self) - self.spbKeepOnePerDay.setRange(1, 10000) - smlayout.addWidget(self.spbKeepOnePerDay, 2, 1) - smlayout.addWidget(QLabel(_('day(s)'), self), 2, 2) - - smlayout.addWidget(QLabel(_('Keep one snapshot per week for the last'), self), 3, 0) - self.spbKeepOnePerWeek = QSpinBox(self) - self.spbKeepOnePerWeek.setRange(1, 10000) - smlayout.addWidget(self.spbKeepOnePerWeek, 3, 1) - smlayout.addWidget(QLabel(_('weeks(s)'), self), 3, 2) - - smlayout.addWidget(QLabel(_('Keep one snapshot per month for the last'), self), 4, 0) - self.spbKeepOnePerMonth = QSpinBox(self) - self.spbKeepOnePerMonth.setRange(1, 1000) - smlayout.addWidget(self.spbKeepOnePerMonth, 4, 1) - smlayout.addWidget(QLabel(_('month(s)'), self), 4, 2) - - smlayout.addWidget(QLabel(_('Keep one snapshot per year for all years'), self), 5, 0, 1, 3) - - enabled = lambda state: [smlayout.itemAt(x).widget().setEnabled(state) for x in range(smlayout.count())] - enabled(False) - self.cbSmartRemove.stateChanged.connect(enabled) - - #don't remove named snapshots - self.cbDontRemoveNamedSnapshots = QCheckBox(_("Don't remove named snapshots"), self) - layout.addWidget(self.cbDontRemoveNamedSnapshots, 5, 0, 1, 3) - - # - layout.addWidget(QWidget(self), 6, 0) - layout.setRowStretch(6, 2) - scrollArea.setWidget(layoutWidget) - scrollArea.setWidgetResizable(True) - - #TAB: Options - scrollArea = QScrollArea(self) - scrollArea.setFrameStyle(QFrame.NoFrame) - self.tabs.addTab(scrollArea, _('Options')) - - layoutWidget = QWidget(self) - layout = QVBoxLayout(layoutWidget) - - self.cbNotify = QCheckBox(_('Enable notifications'), self) - layout.addWidget(self.cbNotify) - - self.cbNoSnapshotOnBattery = QCheckBox(_('Disable snapshots when on battery'), self) - if not tools.powerStatusAvailable (): - self.cbNoSnapshotOnBattery.setEnabled (False) - self.cbNoSnapshotOnBattery.setToolTip (_('Power status not available from system')) - layout.addWidget(self.cbNoSnapshotOnBattery) - - self.cbGlobalFlock = QCheckBox(_('Run only one snapshot at a time')) - self.cbGlobalFlock.setToolTip(_('Other snapshots will be blocked until the current snapshot is done.\n' - 'This is a global option. So it will effect all profiles for this user.\n' - 'But you need to activate this for all other users, too.')) - layout.addWidget(self.cbGlobalFlock) - - self.cbBackupOnRestore = QCheckBox(_('Backup replaced files on restore'), self) - self.cbBackupOnRestore.setToolTip(_("Newer versions of files will be " - "renamed with trailing '%(suffix)s' " - "before restoring.\n" - "If you don't need them anymore " - "you can remove them with '%(cmd)s'") - %{'suffix': self.snapshots.backupSuffix(), - 'cmd': 'find ./ -name "*%s" -delete' % self.snapshots.backupSuffix() }) - layout.addWidget(self.cbBackupOnRestore) - - self.cbContinueOnErrors = QCheckBox(_('Continue on errors (keep incomplete snapshots)'), self) - layout.addWidget(self.cbContinueOnErrors) - - self.cbUseChecksum = QCheckBox(_('Use checksum to detect changes'), self) - layout.addWidget(self.cbUseChecksum) - - self.cbTakeSnapshotRegardlessOfChanges = QCheckBox(_('Take a new snapshot regardless of there were changes or not.')) - layout.addWidget(self.cbTakeSnapshotRegardlessOfChanges) - - #log level - hlayout = QHBoxLayout() - layout.addLayout(hlayout) - - hlayout.addWidget(QLabel(_('Log Level:'), self)) - - self.comboLogLevel = QComboBox(self) - hlayout.addWidget(self.comboLogLevel, 1) - - self.comboLogLevel.addItem(QIcon(), _('None'), 0) - self.comboLogLevel.addItem(QIcon(), _('Errors'), 1) - self.comboLogLevel.addItem(QIcon(), _('Changes & Errors'), 2) - self.comboLogLevel.addItem(QIcon(), _('All'), 3) - - # - layout.addStretch() - scrollArea.setWidget(layoutWidget) - scrollArea.setWidgetResizable(True) - - #TAB: Expert Options - scrollArea = QScrollArea(self) - scrollArea.setFrameStyle(QFrame.NoFrame) - self.tabs.addTab(scrollArea, _('Expert Options')) - - layoutWidget = QWidget(self) - layout = QVBoxLayout(layoutWidget) - - label = QLabel(_('Change these options only if you really know what you are doing !'), self) - qttools.setFontBold(label) - layout.addWidget(label) - - label = QLabel(_("Run 'rsync' with 'nice':")) - layout.addWidget(label) - grid = QGridLayout() - grid.setColumnMinimumWidth(0, 20) - layout.addLayout(grid) - - self.cbNiceOnCron = QCheckBox(_('as cron job') + self.printDefault(self.config.DEFAULT_RUN_NICE_FROM_CRON), self) - grid.addWidget(self.cbNiceOnCron, 0, 1) - - self.cbNiceOnRemote = QCheckBox(_('on remote host') + self.printDefault(self.config.DEFAULT_RUN_NICE_ON_REMOTE), self) - grid.addWidget(self.cbNiceOnRemote, 1, 1) - - label = QLabel(_("Run 'rsync' with 'ionice':")) - layout.addWidget(label) - grid = QGridLayout() - grid.setColumnMinimumWidth(0, 20) - layout.addLayout(grid) - - self.cbIoniceOnCron = QCheckBox(_('as cron job') + self.printDefault(self.config.DEFAULT_RUN_IONICE_FROM_CRON), self) - grid.addWidget(self.cbIoniceOnCron, 0, 1) - - self.cbIoniceOnUser = QCheckBox(_('when taking a manual snapshot') + self.printDefault(self.config.DEFAULT_RUN_IONICE_FROM_USER), self) - grid.addWidget(self.cbIoniceOnUser, 1, 1) - - self.cbIoniceOnRemote = QCheckBox(_('on remote host') + self.printDefault(self.config.DEFAULT_RUN_IONICE_ON_REMOTE), self) - grid.addWidget(self.cbIoniceOnRemote, 2, 1) - - self.nocacheAvailable = tools.checkCommand('nocache') - txt = _("Run 'rsync' with 'nocache':") - if not self.nocacheAvailable: - txt += ' ' + _("(Please install 'nocache' to enable this option)") - layout.addWidget(QLabel(txt)) - grid = QGridLayout() - grid.setColumnMinimumWidth(0, 20) - layout.addLayout(grid) - - self.cbNocacheOnLocal = QCheckBox(_('on local machine') + self.printDefault(self.config.DEFAULT_RUN_NOCACHE_ON_LOCAL), self) - self.cbNocacheOnLocal.setEnabled(self.nocacheAvailable) - grid.addWidget(self.cbNocacheOnLocal, 0, 1) - - self.cbNocacheOnRemote = QCheckBox(_('on remote host') + self.printDefault(self.config.DEFAULT_RUN_NOCACHE_ON_REMOTE), self) - grid.addWidget(self.cbNocacheOnRemote, 2, 1) - - self.cbRedirectStdoutInCron = QCheckBox(_('Redirect stdout to /dev/null in cronjobs.') - + self.printDefault(self.config.DEFAULT_REDIRECT_STDOUT_IN_CRON), - self) - self.cbRedirectStdoutInCron.setToolTip('cron will automatically send an email with attached output of cronjobs if a MTA is installed.') - layout.addWidget(self.cbRedirectStdoutInCron) - - self.cbRedirectStderrInCron = QCheckBox(_('Redirect stderr to /dev/null in cronjobs.') - + self.printDefault(self.config.DEFAULT_REDIRECT_STDERR_IN_CRON), - self) - self.cbRedirectStderrInCron.setToolTip('cron will automatically send an email with attached errors of cronjobs if a MTA is installed.') - layout.addWidget(self.cbRedirectStderrInCron) - - #bwlimit - hlayout = QHBoxLayout() - layout.addLayout(hlayout) - self.cbBwlimit = QCheckBox(_('Limit rsync bandwidth usage: '), self) - hlayout.addWidget(self.cbBwlimit) - self.spbBwlimit = QSpinBox(self) - self.spbBwlimit.setSuffix(_(' KB/sec')) - self.spbBwlimit.setSingleStep(100) - self.spbBwlimit.setRange(0, 1000000) - hlayout.addWidget(self.spbBwlimit) - hlayout.addStretch() - enabled = lambda state: self.spbBwlimit.setEnabled(state) - enabled(False) - self.cbBwlimit.stateChanged.connect(enabled) - self.cbBwlimit.setToolTip( - 'uses \'rsync --bwlimit=RATE\'\n' - 'From \'man rsync\':\n' - 'This option allows you to specify the maximum transfer rate for\n' - 'the data sent over the socket, specified in units per second.\n' - 'The RATE value can be suffixed with a string to indicate a size\n' - 'multiplier, and may be a fractional value (e.g. "--bwlimit=1.5m").\n' - 'If no suffix is specified, the value will be assumed to be in\n' - 'units of 1024 bytes (as if "K" or "KiB" had been appended).\n' - 'See the --max-size option for a description of all the available\n' - 'suffixes. A value of zero specifies no limit.\n\n' - 'For backward-compatibility reasons, the rate limit will be\n' - 'rounded to the nearest KiB unit, so no rate smaller than\n' - '1024 bytes per second is possible.\n\n' - 'Rsync writes data over the socket in blocks, and this option\n' - 'both limits the size of the blocks that rsync writes, and tries\n' - 'to keep the average transfer rate at the requested limit.\n' - 'Some "burstiness" may be seen where rsync writes out a block\n' - 'of data and then sleeps to bring the average rate into compliance.\n\n' - 'Due to the internal buffering of data, the --progress option\n' - 'may not be an accurate reflection on how fast the data is being\n' - 'sent. This is because some files can show up as being rapidly\n' - 'sent when the data is quickly buffered, while other can show up\n' - 'as very slow when the flushing of the output buffer occurs.\n' - 'This may be fixed in a future version.' - ) - - self.cbPreserveAcl = QCheckBox(_('Preserve ACL'), self) - self.cbPreserveAcl.setToolTip( - 'uses \'rsync -A\'\n' - 'From \'man rsync\':\n' - 'This option causes rsync to update the destination ACLs to be\n' - 'the same as the source ACLs. The option also implies --perms.\n\n' - 'The source and destination systems must have compatible ACL\n' - 'entries for this option to work properly.\n' - 'See the --fake-super option for a way to backup and restore\n' - 'ACLs that are not compatible.' - ) - layout.addWidget(self.cbPreserveAcl) - - self.cbPreserveXattr = QCheckBox(_('Preserve extended attributes (xattr)'), self) - self.cbPreserveXattr.setToolTip( - 'uses \'rsync -X\'\n' - 'From \'man rsync\':\n' - 'This option causes rsync to update the destination extended\n' - 'attributes to be the same as the source ones.\n\n' - 'For systems that support extended-attribute namespaces, a copy\n' - 'being done by a super-user copies all namespaces except\n' - 'system.*. A normal user only copies the user.* namespace.\n' - 'To be able to backup and restore non-user namespaces as a normal\n' - 'user, see the --fake-super option.\n\n' - 'Note that this option does not copy rsyncs special xattr values\n' - '(e.g. those used by --fake-super) unless you repeat the option\n' - '(e.g. -XX). This "copy all xattrs" mode cannot be used\n' - 'with --fake-super.' - ) - layout.addWidget(self.cbPreserveXattr) - - self.cbCopyUnsafeLinks = QCheckBox(_('Copy unsafe links (works only with absolute links)'), self) - self.cbCopyUnsafeLinks.setToolTip( - 'uses \'rsync --copy-unsafe-links\'\n' - 'From \'man rsync\':\n' - 'This tells rsync to copy the referent of symbolic links that\n' - 'point outside the copied tree. Absolute symlinks are also\n' - 'treated like ordinary files, and so are any symlinks in the\n' - 'source path itself when --relative is used. This option has\n' - 'no additional effect if --copy-links was also specified.\n' - ) - layout.addWidget(self.cbCopyUnsafeLinks) - - self.cbCopyLinks = QCheckBox(_('Copy links (dereference symbolic links)'), self) - self.cbCopyLinks.setToolTip( - 'uses \'rsync --copy-links\'\n' - 'From \'man rsync\':\n' - 'When symlinks are encountered, the item that they point to\n' - '(the referent) is copied, rather than the symlink. In older\n' - 'versions of rsync, this option also had the side-effect of\n' - 'telling the receiving side to follow symlinks, such as\n' - 'symlinks to directories. In a modern rsync such as this one,\n' - 'you\'ll need to specify --keep-dirlinks (-K) to get this extra\n' - 'behavior. The only exception is when sending files to an rsync\n' - 'that is too old to understand -K -- in that case, the -L option\n' - 'will still have the side-effect of -K on that older receiving rsync.' - ) - layout.addWidget(self.cbCopyLinks) - - #additional rsync options - hlayout = QHBoxLayout() - layout.addLayout(hlayout) - self.cbRsyncOptions = QCheckBox(_('Paste additional options to rsync'), self) - hlayout.addWidget(self.cbRsyncOptions) - self.txtRsyncOptions = QLineEdit(self) - self.txtRsyncOptions.setToolTip(_('Options must be quoted e.g. --exclude-from="/path/to/my exclude file".')) - hlayout.addWidget(self.txtRsyncOptions) - - enabled = lambda state: self.txtRsyncOptions.setEnabled(state) - enabled(False) - self.cbRsyncOptions.stateChanged.connect(enabled) - - #ssh prefix - hlayout = QHBoxLayout() - layout.addLayout(hlayout) - self.cbSshPrefix = QCheckBox(_('Add prefix to SSH commands'), self) - hlayout.addWidget(self.cbSshPrefix) - self.txtSshPrefix = QLineEdit(self) - self.txtSshPrefix.setToolTip(_('Prefix to run before every command on remote host.\n' - 'Variables need to be escaped with \$FOO.\n' - 'This doesn\'t touch rsync. So to add a prefix\n' - 'for rsync use "%(cbRsyncOptions)s" with\n' - '%(rsync_options_value)s\n\n' - '%(default)s: %(def_value)s') - % {'cbRsyncOptions': self.cbRsyncOptions.text(), - 'rsync_options_value': '--rsync-path="FOO=bar:\$FOO /usr/bin/rsync"', - 'default': _('default'), - 'def_value': self.config.DEFAULT_SSH_PREFIX}) - hlayout.addWidget(self.txtSshPrefix) - - enabled = lambda state: self.txtSshPrefix.setEnabled(state) - enabled(False) - self.cbSshPrefix.stateChanged.connect(enabled) - - qttools.equalIndent(self.cbRsyncOptions, self.cbSshPrefix) - - self.cbSshCheckPing = QCheckBox(_('Check if remote host is online')) - self.cbSshCheckPing.setToolTip(_('Warning: if disabled and the remote host\n' - 'is not available, this could lead to some\n' - 'weird errors.')) - self.cbSshCheckCommands = QCheckBox(_('Check if remote host support all necessary commands')) - self.cbSshCheckCommands.setToolTip(_('Warning: if disabled and the remote host\n' - 'does not support all necessary commands,\n' - 'this could lead to some weird errors.')) - layout.addWidget(self.cbSshCheckPing) - layout.addWidget(self.cbSshCheckCommands) - - # - layout.addStretch() - scrollArea.setWidget(layoutWidget) - scrollArea.setWidgetResizable(True) - - #buttons - buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent = self) - btnRestore = buttonBox.addButton(_('Restore Config'), QDialogButtonBox.ResetRole) - btnUserCallback = buttonBox.addButton(_('Edit user-callback'), QDialogButtonBox.ResetRole) - buttonBox.accepted.connect(self.accept) - buttonBox.rejected.connect(self.reject) - btnRestore.clicked.connect(self.restoreConfig) - btnUserCallback.clicked.connect(self.editUserCallback) - self.mainLayout.addWidget(buttonBox) - - self.updateProfiles() - self.comboModesChanged() - - #enable tabs scroll buttons again but keep dialog size - size = self.sizeHint() - self.tabs.setUsesScrollButtons(scrollButtonDefault) - self.resize(size) - - self.finished.connect(self.cleanup) - - def modifyProfileForFullSystemBackup(self): - # verify to user that settings will change - message = _("Full system backup can only create a snapshot to be restored to the same physical disk(s) " - "with the same disk partitioning as from the source; restoring to new physical disks or the same disks " - "with different partitioning will yield a potentially broken and unusable system.\n\n" - "Full system backup will override some settings that may have been customized. Continue?") - if QMessageBox.No == messagebox.warningYesNo(self, message): - return - - # configure for full system backup - # don't want to create a backup with any errors and give user false sense that backup was ok - self.config.setContinueOnErrors(False) - # make sure all files are backed up - self.config.setExcludeBySize(False, 500) - # when we restore the full system, don't want to keep old files (since we back up everything) - self.config.setBackupOnRestore(False) - # no need for checksum mode - self.config.setUseChecksum(False) - # must preserve ACLs and xattrs - self.config.setPreserveAcl(True) - self.config.setPreserveXattr(True) - # don't want links - self.config.setCopyLinks(False) - self.config.setCopyUnsafeLinks(False) - # backup root - self.config.setInclude([("/", 0)]) - - # set UI - self.updateProfiles() - - def addProfile(self): - ret_val = QInputDialog.getText(self, _('New profile'), str()) - if not ret_val[1]: - return - - name = ret_val[0].strip() - if not name: - return - - profile_id = self.config.addProfile(name) - if profile_id is None: - return - - self.config.setCurrentProfile(profile_id) - self.updateProfiles() - - def editProfile(self): - ret_val = QInputDialog.getText(self, _('Rename profile'), str(), text = self.config.profileName()) - if not ret_val[1]: - return - - name = ret_val[0].strip() - if not name: - return - - if not self.config.setProfileName(name): - return - - self.updateProfiles(reloadSettings = False) - - def removeProfile(self): - if self.questionHandler(_('Are you sure you want to delete the profile "%s" ?') % self.config.profileName()): - self.config.removeProfile() - self.updateProfiles() - - def updateSchedule(self, backup_mode): - if backup_mode == self.config.CUSTOM_HOUR: - self.lblScheduleCronPatern.show() - self.txtScheduleCronPatern.show() - else: - self.lblScheduleCronPatern.hide() - self.txtScheduleCronPatern.hide() - - if backup_mode == self.config.WEEK: - self.lblScheduleWeekday.show() - self.comboScheduleWeekday.show() - else: - self.lblScheduleWeekday.hide() - self.comboScheduleWeekday.hide() - - if backup_mode == self.config.MONTH: - self.lblScheduleDay.show() - self.comboScheduleDay.show() - else: - self.lblScheduleDay.hide() - self.comboScheduleDay.hide() - - if backup_mode >= self.config.DAY: - self.lblScheduleTime.show() - self.comboScheduleTime.show() - else: - self.lblScheduleTime.hide() - self.comboScheduleTime.hide() - - if self.config.REPEATEDLY <= backup_mode <= self.config.UDEV: - self.lblScheduleRepeatedPeriod.show() - self.spbScheduleRepeatedPeriod.show() - self.comboScheduleRepeatedUnit.show() - self.lblScheduleTime.hide() - self.comboScheduleTime.hide() - else: - self.lblScheduleRepeatedPeriod.hide() - self.spbScheduleRepeatedPeriod.hide() - self.comboScheduleRepeatedUnit.hide() - - if backup_mode == self.config.REPEATEDLY: - self.lblScheduleRepeated.show() - else: - self.lblScheduleRepeated.hide() - - if backup_mode == self.config.UDEV: - self.lblScheduleUdev.show() - else: - self.lblScheduleUdev.hide() - - def scheduleChanged(self, index): - backup_mode = self.comboSchedule.itemData(index) - self.updateSchedule(backup_mode) - - def profileChanged(self, index): - if self.disableProfileChanged: - return - - profile_id = self.comboProfiles.currentProfileID() - if not profile_id: - return - - if profile_id != self.config.currentProfile(): - self.saveProfile() - self.config.setCurrentProfile(profile_id) - self.updateProfile() - - def updateProfiles(self, reloadSettings = True): - if reloadSettings: - self.updateProfile() - current_profile_id = self.config.currentProfile() - - self.disableProfileChanged = True - - self.comboProfiles.clear() - - profiles = self.config.profilesSortedByName() - for profile_id in profiles: - self.comboProfiles.addProfileID(profile_id) - if profile_id == current_profile_id: - self.comboProfiles.setCurrentProfileID(profile_id) - - self.disableProfileChanged = False - - def updateProfile(self): - if self.config.currentProfile() == '1': - self.btnEditProfile.setEnabled(False) - self.btnRemoveProfile.setEnabled(False) - else: - self.btnEditProfile.setEnabled(True) - self.btnRemoveProfile.setEnabled(True) - self.btnAddProfile.setEnabled(self.config.isConfigured('1')) - - #TAB: General - #mode - self.setComboValue(self.comboModes, self.config.snapshotsMode(), t = 'str') - - #local - self.editSnapshotsPath.setText(self.config.snapshotsPath(mode = 'local')) - - #ssh - self.txtSshHost.setText(self.config.sshHost()) - self.txtSshPort.setText(str(self.config.sshPort())) - self.txtSshUser.setText(self.config.sshUser()) - self.txtSshPath.setText(self.config.sshSnapshotsPath()) - self.setComboValue(self.comboSshCipher, self.config.sshCipher(), t = 'str') - self.txtSshPrivateKeyFile.setText(self.config.sshPrivateKeyFile()) - - #local_encfs - if self.mode == 'local_encfs': - self.editSnapshotsPath.setText(self.config.localEncfsPath()) - - #password - password_1 = self.config.password(mode = self.mode, pw_id = 1, only_from_keyring = True) - password_2 = self.config.password(mode = self.mode, pw_id = 2, only_from_keyring = True) - if password_1 is None: - password_1 = '' - if password_2 is None: - password_2 = '' - self.txtPassword1.setText(password_1) - self.txtPassword2.setText(password_2) - self.cbPasswordSave.setChecked(self.keyringSupported and self.config.passwordSave(mode = self.mode)) - self.cbPasswordUseCache.setChecked(self.config.passwordUseCache(mode = self.mode)) - - host, user, profile = self.config.hostUserProfile() - self.txtHost.setText(host) - self.txtUser.setText(user) - self.txt_profile.setText(profile) - - self.setComboValue(self.comboSchedule, self.config.scheduleMode()) - self.setComboValue(self.comboScheduleTime, self.config.scheduleTime()) - self.setComboValue(self.comboScheduleDay, self.config.scheduleDay()) - self.setComboValue(self.comboScheduleWeekday, self.config.scheduleWeekday()) - self.txtScheduleCronPatern.setText(self.config.customBackupTime()) - self.spbScheduleRepeatedPeriod.setValue(self.config.scheduleRepeatedPeriod()) - self.setComboValue(self.comboScheduleRepeatedUnit, self.config.scheduleRepeatedUnit()) - self.updateSchedule(self.config.scheduleMode()) - - #TAB: Include - self.listInclude.clear() - - for include in self.config.include(): - self.addInclude(include) - - includeSortColumn = int(self.config.profileIntValue('qt.settingsdialog.include.SortColumn', 1)) - includeSortOrder = int(self.config.profileIntValue('qt.settingsdialog.include.SortOrder', Qt.AscendingOrder)) - self.listInclude.sortItems(includeSortColumn, includeSortOrder) - - #TAB: Exclude - self.listExclude.clear() - - for exclude in self.config.exclude(): - self.addExclude(exclude) - self.cbExcludeBySize.setChecked(self.config.excludeBySizeEnabled()) - self.spbExcludeBySize.setValue(self.config.excludeBySize()) - - excludeSortColumn = int(self.config.profileIntValue('qt.settingsdialog.exclude.SortColumn', 1)) - excludeSortOrder = int(self.config.profileIntValue('qt.settingsdialog.exclude.SortOrder', Qt.AscendingOrder)) - self.listExclude.sortItems(excludeSortColumn, excludeSortOrder) - - #TAB: Auto-remove - - #remove old snapshots - enabled, value, unit = self.config.removeOldSnapshots() - self.cbRemoveOlder.setChecked(enabled) - self.spbRemoveOlder.setValue(value) - self.setComboValue(self.comboRemoveOlderUnit, unit) - - #min free space - enabled, value, unit = self.config.minFreeSpace() - self.cbFreeSpace.setChecked(enabled) - self.spbFreeSpace.setValue(value) - self.setComboValue(self.comboFreeSpaceUnit, unit) - - #min free inodes - self.cbFreeInodes.setChecked(self.config.minFreeInodesEnabled()) - self.spbFreeInodes.setValue(self.config.minFreeInodes()) - - #smart remove - smart_remove, keep_all, keep_one_per_day, keep_one_per_week, keep_one_per_month = self.config.smartRemove() - self.cbSmartRemove.setChecked(smart_remove) - self.spbKeepAll.setValue(keep_all) - self.spbKeepOnePerDay.setValue(keep_one_per_day) - self.spbKeepOnePerWeek.setValue(keep_one_per_week) - self.spbKeepOnePerMonth.setValue(keep_one_per_month) - self.cbSmartRemoveRunRemoteInBackground.setChecked(self.config.smartRemoveRunRemoteInBackground()) - - #don't remove named snapshots - self.cbDontRemoveNamedSnapshots.setChecked(self.config.dontRemoveNamedSnapshots()) - - #TAB: Options - self.cbNotify.setChecked(self.config.notify()) - self.cbNoSnapshotOnBattery.setChecked(self.config.noSnapshotOnBattery()) - self.cbGlobalFlock.setChecked(self.config.globalFlock()) - self.cbBackupOnRestore.setChecked(self.config.backupOnRestore()) - self.cbContinueOnErrors.setChecked(self.config.continueOnErrors()) - self.cbUseChecksum.setChecked(self.config.useChecksum()) - self.cbTakeSnapshotRegardlessOfChanges.setChecked(self.config.takeSnapshotRegardlessOfChanges()) - self.setComboValue(self.comboLogLevel, self.config.logLevel()) - - #TAB: Expert Options - self.cbNiceOnCron.setChecked(self.config.niceOnCron()) - self.cbIoniceOnCron.setChecked(self.config.ioniceOnCron()) - self.cbIoniceOnUser.setChecked(self.config.ioniceOnUser()) - self.cbNiceOnRemote.setChecked(self.config.niceOnRemote()) - self.cbIoniceOnRemote.setChecked(self.config.ioniceOnRemote()) - self.cbNocacheOnLocal.setChecked(self.config.nocacheOnLocal() and self.nocacheAvailable) - self.cbNocacheOnRemote.setChecked(self.config.nocacheOnRemote()) - self.cbRedirectStdoutInCron.setChecked(self.config.redirectStdoutInCron()) - self.cbRedirectStderrInCron.setChecked(self.config.redirectStderrInCron()) - self.cbBwlimit.setChecked(self.config.bwlimitEnabled()) - self.spbBwlimit.setValue(self.config.bwlimit()) - self.cbPreserveAcl.setChecked(self.config.preserveAcl()) - self.cbPreserveXattr.setChecked(self.config.preserveXattr()) - self.cbCopyUnsafeLinks.setChecked(self.config.copyUnsafeLinks()) - self.cbCopyLinks.setChecked(self.config.copyLinks()) - self.cbRsyncOptions.setChecked(self.config.rsyncOptionsEnabled()) - self.txtRsyncOptions.setText(self.config.rsyncOptions()) - self.cbSshPrefix.setChecked(self.config.sshPrefixEnabled()) - self.txtSshPrefix.setText(self.config.sshPrefix()) - self.cbSshCheckPing.setChecked(self.config.sshCheckPingHost()) - self.cbSshCheckCommands.setChecked(self.config.sshCheckCommands()) - - #update - self.updateRemoveOlder() - self.updateFreeSpace() - - def saveProfile(self): - if self.comboSchedule.itemData(self.comboSchedule.currentIndex()) == self.config.CUSTOM_HOUR: - if not tools.checkCronPattern(self.txtScheduleCronPatern.text()): - self.errorHandler(_('Custom Hours can only be a comma separated list of hours (e.g. 8,12,18,23) or */3 for periodic backups every 3 hours')) - return False - - #mode - mode = str(self.comboModes.itemData(self.comboModes.currentIndex())) - self.config.setSnapshotsMode(mode) - mount_kwargs = {} - - #password - password_1 = self.txtPassword1.text() - password_2 = self.txtPassword2.text() - - - if mode in ('ssh', 'local_encfs'): - mount_kwargs = {'password': password_1 - } - - if mode == 'ssh_encfs': - mount_kwargs = {'ssh_password': password_1, - 'encfs_password': password_2, - } - - #snapshots path - self.config.setHostUserProfile( - self.txtHost.text(), - self.txtUser.text(), - self.txt_profile.text()) - - #save ssh - self.config.setSshHost(self.txtSshHost.text()) - self.config.setSshPort(self.txtSshPort.text()) - self.config.setSshUser(self.txtSshUser.text()) - self.config.setSshSnapshotsPath(self.txtSshPath.text()) - self.config.setSshCipher(self.comboSshCipher.itemData(self.comboSshCipher.currentIndex())) - if mode in ('ssh', 'ssh_encfs'): - if not self.txtSshPrivateKeyFile.text(): - if self.questionHandler(_('You did not choose a private key file for SSH.\nWould you like to generate a new password-less public/private key pair?')): - self.btnSshKeyGenClicked() - if not self.txtSshPrivateKeyFile.text(): - return False - if not os.path.isfile(self.txtSshPrivateKeyFile.text()): - self.errorHandler(_('Private key file "%(file)s" does not exist.') - %{'file': self.txtSshPrivateKeyFile.text()}) - self.txtSshPrivateKeyFile.setText('') - return False - self.config.setSshPrivateKeyFile(self.txtSshPrivateKeyFile.text()) - - #save local_encfs - self.config.setLocalEncfsPath(self.editSnapshotsPath.text()) - - #include list - self.config.setProfileIntValue('qt.settingsdialog.include.SortColumn', - self.listInclude.header().sortIndicatorSection()) - self.config.setProfileIntValue('qt.settingsdialog.include.SortOrder', - self.listInclude.header().sortIndicatorOrder()) - self.listInclude.sortItems(1, Qt.AscendingOrder) - - include_list = [] - for index in range(self.listInclude.topLevelItemCount()): - item = self.listInclude.topLevelItem(index) - include_list.append((item.text(0), item.data(0, Qt.UserRole))) - - self.config.setInclude(include_list) - - #exclude patterns - self.config.setProfileIntValue('qt.settingsdialog.exclude.SortColumn', - self.listExclude.header().sortIndicatorSection()) - self.config.setProfileIntValue('qt.settingsdialog.exclude.SortOrder', - self.listExclude.header().sortIndicatorOrder()) - self.listExclude.sortItems(1, Qt.AscendingOrder) - - exclude_list = [] - for index in range(self.listExclude.topLevelItemCount()): - item = self.listExclude.topLevelItem(index) - exclude_list.append(item.text(0)) - - self.config.setExclude(exclude_list) - self.config.setExcludeBySize(self.cbExcludeBySize.isChecked(), - self.spbExcludeBySize.value()) - - #schedule - self.config.setScheduleMode(self.comboSchedule.itemData(self.comboSchedule.currentIndex())) - self.config.setScheduleTime(self.comboScheduleTime.itemData(self.comboScheduleTime.currentIndex())) - self.config.setScheduleWeekday(self.comboScheduleWeekday.itemData(self.comboScheduleWeekday.currentIndex())) - self.config.setScheduleDay(self.comboScheduleDay.itemData(self.comboScheduleDay.currentIndex())) - self.config.setCustomBackupTime(self.txtScheduleCronPatern.text()) - self.config.setScheduleRepeatedPeriod(self.spbScheduleRepeatedPeriod.value()) - self.config.setScheduleRepeatedUnit(self.comboScheduleRepeatedUnit.itemData(self.comboScheduleRepeatedUnit.currentIndex())) - - #auto-remove - self.config.setRemoveOldSnapshots( - self.cbRemoveOlder.isChecked(), - self.spbRemoveOlder.value(), - self.comboRemoveOlderUnit.itemData(self.comboRemoveOlderUnit.currentIndex())) - self.config.setMinFreeSpace( - self.cbFreeSpace.isChecked(), - self.spbFreeSpace.value(), - self.comboFreeSpaceUnit.itemData(self.comboFreeSpaceUnit.currentIndex())) - self.config.setMinFreeInodes( - self.cbFreeInodes.isChecked(), - self.spbFreeInodes.value()) - self.config.setDontRemoveNamedSnapshots(self.cbDontRemoveNamedSnapshots.isChecked()) - self.config.setSmartRemove( - self.cbSmartRemove.isChecked(), - self.spbKeepAll.value(), - self.spbKeepOnePerDay.value(), - self.spbKeepOnePerWeek.value(), - self.spbKeepOnePerMonth.value()) - self.config.setSmartRemoveRunRemoteInBackground(self.cbSmartRemoveRunRemoteInBackground.isChecked()) - - #options - self.config.setNotify(self.cbNotify.isChecked()) - self.config.setNoSnapshotOnBattery(self.cbNoSnapshotOnBattery.isChecked()) - self.config.setGlobalFlock(self.cbGlobalFlock.isChecked()) - self.config.setBackupOnRestore(self.cbBackupOnRestore.isChecked()) - self.config.setContinueOnErrors(self.cbContinueOnErrors.isChecked()) - self.config.setUseChecksum(self.cbUseChecksum.isChecked()) - self.config.setTakeSnapshotRegardlessOfChanges(self.cbTakeSnapshotRegardlessOfChanges.isChecked()) - self.config.setLogLevel(self.comboLogLevel.itemData(self.comboLogLevel.currentIndex())) - - #expert options - self.config.setNiceOnCron(self.cbNiceOnCron.isChecked()) - self.config.setIoniceOnCron(self.cbIoniceOnCron.isChecked()) - self.config.setIoniceOnUser(self.cbIoniceOnUser.isChecked()) - self.config.setNiceOnRemote(self.cbNiceOnRemote.isChecked()) - self.config.setIoniceOnRemote(self.cbIoniceOnRemote.isChecked()) - self.config.setNocacheOnLocal(self.cbNocacheOnLocal.isChecked()) - self.config.setNocacheOnRemote(self.cbNocacheOnRemote.isChecked()) - self.config.setRedirectStdoutInCron(self.cbRedirectStdoutInCron.isChecked()) - self.config.setRedirectStderrInCron(self.cbRedirectStderrInCron.isChecked()) - self.config.setBwlimit(self.cbBwlimit.isChecked(), - self.spbBwlimit.value()) - self.config.setPreserveAcl(self.cbPreserveAcl.isChecked()) - self.config.setPreserveXattr(self.cbPreserveXattr.isChecked()) - self.config.setCopyUnsafeLinks(self.cbCopyUnsafeLinks.isChecked()) - self.config.setCopyLinks(self.cbCopyLinks.isChecked()) - self.config.setRsyncOptions(self.cbRsyncOptions.isChecked(), - self.txtRsyncOptions.text()) - self.config.setSshPrefix(self.cbSshPrefix.isChecked(), - self.txtSshPrefix.text()) - self.config.setSshCheckPingHost(self.cbSshCheckPing.isChecked()) - self.config.setSshCheckCommands(self.cbSshCheckCommands.isChecked()) - - # TODO - consider a single API method to bridge the UI layer (settings dialog) and backend layer (config) - # when setting snapshots path rather than having to call the mount module from the UI layer - # - # currently, setting snapshots path requires the path to be mounted. it seems that it might be nice, - # since the config object is more than a data structure, but has side-effect logic as well, to have the - # config.setSnapshotsPath() method take care of everything it needs to perform its job - # (mounting and unmounting the fuse filesystem if necessary). - # https://en.wikipedia.org/wiki/Single_responsibility_principle - - if not self.config.SNAPSHOT_MODES[mode][0] is None: - #preMountCheck - mnt = mount.Mount(cfg = self.config, tmp_mount = True, parent = self) - try: - mnt.preMountCheck(mode = mode, first_run = True, **mount_kwargs) - except NoPubKeyLogin as ex: - logger.error(str(ex), self) - if self.questionHandler(_('Would you like to copy your public SSH key to the\nremote host to enable password-less login?')) \ - and sshtools.sshCopyId(self.config.sshPrivateKeyFile() + '.pub', - self.config.sshUser(), - self.config.sshHost(), - port = str(self.config.sshPort()), - askPass = tools.which('backintime-askpass'), - cipher = self.config.sshCipher()): - return self.saveProfile() - else: - return False - except KnownHost as ex: - logger.error(str(ex), self) - fingerprint, hashedKey, keyType = sshtools.sshHostKey(self.config.sshHost(), - str(self.config.sshPort())) - if not fingerprint: - self.errorHandler(str(ex)) - return False - msg = _('The authenticity of host "%(host)s" can\'t be established.\n\n%(keytype)s key fingerprint is:') - msg = msg %{'host': self.config.sshHost(), - 'keytype': keyType} - options = [] - lblFingerprint = QLabel(fingerprint + '\n') - lblFingerprint.setWordWrap(False) - lblFingerprint.setFont(QFont('Monospace')) - options.append({'widget': lblFingerprint, 'retFunc': None}) - lblQuestion = QLabel(_('Please verify this fingerprint! Would you like to add it to your \'known_hosts\' file?')) - options.append({'widget': lblQuestion, 'retFunc': None}) - if messagebox.warningYesNoOptions(self, msg, options)[0]: - sshtools.writeKnownHostsFile(hashedKey) - return self.saveProfile() - else: - return False - except MountException as ex: - self.errorHandler(str(ex)) - return False - - #okay, lets try to mount - try: - hash_id = mnt.mount(mode = mode, check = False, **mount_kwargs) - except MountException as ex: - self.errorHandler(str(ex)) - return False - - #save password - self.config.setPasswordSave(self.cbPasswordSave.isChecked(), mode = mode) - self.config.setPasswordUseCache(self.cbPasswordUseCache.isChecked(), mode = mode) - self.config.setPassword(password_1, mode = mode) - self.config.setPassword(password_2, mode = mode, pw_id = 2) - - #save snaphots_path - if self.config.SNAPSHOT_MODES[mode][0] is None: - snapshots_path = self.editSnapshotsPath.text() - else: - snapshots_path = self.config.snapshotsPath(mode = mode, tmp_mount = True) - - ret = self.config.setSnapshotsPath(snapshots_path, mode = mode) - if not ret: - return ret - - #umount - if not self.config.SNAPSHOT_MODES[mode][0] is None: - try: - mnt.umount(hash_id = hash_id) - except MountException as ex: - self.errorHandler(str(ex)) - return False - return True - - def errorHandler(self, message): - messagebox.critical(self, message) - - def questionHandler(self, message): - return QMessageBox.Yes == messagebox.warningYesNo(self, message) - - def updateRemoveOlder(self): - enabled = self.cbRemoveOlder.isChecked() - self.spbRemoveOlder.setEnabled(enabled) - self.comboRemoveOlderUnit.setEnabled(enabled) - - def updateFreeSpace(self): - enabled = self.cbFreeSpace.isChecked() - self.spbFreeSpace.setEnabled(enabled) - self.comboFreeSpaceUnit.setEnabled(enabled) - - def addInclude(self, data): - item = QTreeWidgetItem() - - if data[1] == 0: - item.setIcon(0, self.icon.FOLDER) - else: - item.setIcon(0, self.icon.FILE) - - item.setText(0, data[0]) - item.setData(0, Qt.UserRole, data[1]) - self.listIncludeCount += 1 - item.setText(1, str(self.listIncludeCount).zfill(6)) - item.setData(1, Qt.UserRole, self.listIncludeCount) - self.listInclude.addTopLevelItem(item) - - if self.listInclude.currentItem() is None: - self.listInclude.setCurrentItem(item) - - return item - - def addExclude(self, pattern): - item = QTreeWidgetItem() - item.setText(0, pattern) - item.setData(0, Qt.UserRole, pattern) - self.listExcludeCount += 1 - item.setText(1, str(self.listExcludeCount).zfill(6)) - item.setData(1, Qt.UserRole, self.listExcludeCount) - self.formatExcludeItem(item) - self.listExclude.addTopLevelItem(item) - - if self.listExclude.currentItem() is None: - self.listExclude.setCurrentItem(item) - - return item - - def fillCombo(self, combo, d): - keys = list(d.keys()) - keys.sort() - - for key in keys: - combo.addItem(QIcon(), d[key], key) - - def setComboValue(self, combo, value, t = 'int'): - for i in range(combo.count()): - if t == 'int' and value == combo.itemData(i): - combo.setCurrentIndex(i) - break - if t == 'str' and value == combo.itemData(i): - combo.setCurrentIndex(i) - break - - def validate(self): - if not self.saveProfile(): - return False - - if not self.config.checkConfig(): - return False - - if not self.config.setupCron(): - return False - - return self.config.save() - - def btnExcludeRemoveClicked(self): - for item in self.listExclude.selectedItems(): - index = self.listExclude.indexOfTopLevelItem(item) - if index < 0: - continue - - self.listExclude.takeTopLevelItem(index) - - if self.listExclude.topLevelItemCount() > 0: - self.listExclude.setCurrentItem(self.listExclude.topLevelItem(0)) - - def addExclude_(self, pattern): - if not pattern: - return - - for index in range(self.listExclude.topLevelItemCount()): - item = self.listExclude.topLevelItem(index) - if pattern == item.text(0): - return - - self.addExclude(pattern) - - def btnExcludeAddClicked(self): - dlg = QInputDialog(self) - dlg.setInputMode(QInputDialog.TextInput) - dlg.setWindowTitle(_('Exclude pattern')) - dlg.setLabelText('') - dlg.resize(400, 0) - if not dlg.exec(): - return - pattern = dlg.textValue().strip() - - if not pattern: - return - - self.addExclude_(pattern) - - def btnExcludeFileClicked(self): - for path in qttools.getOpenFileNames(self, _('Exclude file')): - self.addExclude_(path) - - def btnExcludeFolderClicked(self): - for path in qttools.getExistingDirectories(self, _('Exclude folder')) : - self.addExclude_(path) - - def btnExcludeDefaultClicked(self): - for path in self.config.DEFAULT_EXCLUDE: - self.addExclude_(path) - - def btnIncludeRemoveClicked(self): - for item in self.listInclude.selectedItems(): - index = self.listInclude.indexOfTopLevelItem(item) - if index < 0: - continue - - self.listInclude.takeTopLevelItem(index) - - if self.listInclude.topLevelItemCount() > 0: - self.listInclude.setCurrentItem(self.listInclude.topLevelItem(0)) - - def btnIncludeFileClicked(self): - for path in qttools.getOpenFileNames(self, _('Include file')): - if not path: - continue - - if os.path.islink(path) \ - and not (self.cbCopyUnsafeLinks.isChecked() \ - or self.cbCopyLinks.isChecked()): - if self.questionHandler(\ - _('"%s" is a symlink. The linked target will not be backed up until you include it, too.\nWould you like to include the symlinks target instead?') % path): - path = os.path.realpath(path) - - path = self.config.preparePath(path) - - for index in range(self.listInclude.topLevelItemCount()): - if path == self.listInclude.topLevelItem(index).text(0): - continue - - self.addInclude((path, 1)) - - def btnIncludeAddClicked(self): - for path in qttools.getExistingDirectories(self, _('Include folder')): - if not path: - continue - - if os.path.islink(path) \ - and not (self.cbCopyUnsafeLinks.isChecked() \ - or self.cbCopyLinks.isChecked()): - if self.questionHandler(\ - _('"%s" is a symlink. The linked target will not be backed up until you include it, too.\nWould you like to include the symlinks target instead?') % path): - path = os.path.realpath(path) - - path = self.config.preparePath(path) - - for index in range(self.listInclude.topLevelItemCount()): - if path == self.listInclude.topLevelItem(index).text(0): - continue - - self.addInclude((path, 0)) - - def btnSnapshotsPathClicked(self): - old_path = self.editSnapshotsPath.text() - - path = str(qttools.getExistingDirectory(self, - _('Where to save snapshots'), - self.editSnapshotsPath.text())) - if path: - if old_path and old_path != path: - if not self.questionHandler(_('Are you sure you want to change snapshots folder ?')): - return - self.config.removeProfileKey('snapshots.path.uuid') - self.editSnapshotsPath.setText(self.config.preparePath(path)) - - def btnSshPrivateKeyFileClicked(self): - old_file = self.txtSshPrivateKeyFile.text() - - if old_file: - start_dir = self.txtSshPrivateKeyFile.text() - else: - start_dir = self.config.sshPrivateKeyFolder() - f = qttools.getOpenFileName(self, _('SSH private key'), start_dir) - if f: - self.txtSshPrivateKeyFile.setText(f) - - def btnSshKeyGenClicked(self): - key = os.path.join(self.config.sshPrivateKeyFolder(), 'id_rsa') - if sshtools.sshKeyGen(key): - self.txtSshPrivateKeyFile.setText(key) - else: - self.errorHandler(_('Failed to create new SSH key in %(path)s') %{'path': key}) - - def comboModesChanged(self, *params): - if not params: - index = self.comboModes.currentIndex() - else: - index = params[0] - active_mode = str(self.comboModes.itemData(index)) - if active_mode != self.mode: - for mode in list(self.config.SNAPSHOT_MODES.keys()): - getattr(self, 'mode%s' % tools.camelCase(mode)).hide() - for mode in list(self.config.SNAPSHOT_MODES.keys()): - if active_mode == mode: - getattr(self, 'mode%s' % tools.camelCase(mode)).show() - self.mode = active_mode - - if self.config.modeNeedPassword(active_mode): - self.lblPassword1.setText(self.config.SNAPSHOT_MODES[active_mode][2] + ':') - self.groupPassword1.show() - if self.config.modeNeedPassword(active_mode, 2): - self.lblPassword2.setText(self.config.SNAPSHOT_MODES[active_mode][3] + ':') - self.lblPassword2.show() - self.txtPassword2.show() - qttools.equalIndent(self.lblPassword1, self.lblPassword2) - else: - self.lblPassword2.hide() - self.txtPassword2.hide() - qttools.equalIndent(self.lblPassword1) - else: - self.groupPassword1.hide() - - if active_mode == 'ssh_encfs': - self.lblSshEncfsExcludeWarning.show() - else: - self.lblSshEncfsExcludeWarning.hide() - self.updateExcludeItems() - - enabled = active_mode in ('ssh', 'ssh_encfs') - self.cbNiceOnRemote.setEnabled(enabled) - self.cbIoniceOnRemote.setEnabled(enabled) - self.cbNocacheOnRemote.setEnabled(enabled) - self.cbSmartRemoveRunRemoteInBackground.setHidden(not enabled) - self.cbSshPrefix.setHidden(not enabled) - self.txtSshPrefix.setHidden(not enabled) - self.cbSshCheckPing.setHidden(not enabled) - self.cbSshCheckCommands.setHidden(not enabled) - - self.encfsWarning.setHidden(active_mode not in ('local_encfs', 'ssh_encfs')) - - def fullPathChanged(self, dummy): - if self.mode in ('ssh', 'ssh_encfs'): - path = self.txtSshPath.text() - else: - path = self.editSnapshotsPath.text() - self.lblFullPath.setText( - _('Full snapshot path: ') + - os.path.join( - path, - 'backintime', - self.txtHost.text(), - self.txtUser.text(), - self.txt_profile.text() - )) - - def updateExcludeItems(self): - for index in range(self.listExclude.topLevelItemCount()): - item = self.listExclude.topLevelItem(index) - self.formatExcludeItem(item) - - def formatExcludeItem(self, item): - if self.mode == 'ssh_encfs' and tools.patternHasNotEncryptableWildcard(item.text(0)): - item.setIcon(0, self.icon.INVALID_EXCLUDE) - item.setBackground(0, QPalette().brush(QPalette.Active, QPalette.Link)) - elif item.text(0) in self.config.DEFAULT_EXCLUDE: - item.setIcon(0, self.icon.DEFAULT_EXCLUDE) - item.setBackground(0, QBrush()) - else: - item.setIcon(0, self.icon.EXCLUDE) - item.setBackground(0, QBrush()) - - def customSortOrder(self, header, loop, newColumn, newOrder): - if newColumn == 0 and newOrder == Qt.AscendingOrder: - if loop: - newColumn, newOrder = 1, Qt.AscendingOrder - header.setSortIndicator(newColumn, newOrder) - loop = False - else: - loop = True - header.model().sort(newColumn, newOrder) - return loop - - def includeCustomSortOrder(self, *args): - self.listIncludeSortLoop = self.customSortOrder(self.listInclude.header(), - self.listIncludeSortLoop, - *args) - - def excludeCustomSortOrder(self, *args): - self.listExcludeSortLoop = self.customSortOrder(self.listExclude.header(), - self.listExcludeSortLoop, - *args) - - def printDefault(self, value): - if value: - value_ = _('enabled') - else: - value_ = _('disabled') - return ' (%s: %s)' %(_('default'), value_) - - def restoreConfig(self, *args): - RestoreConfigDialog(self).exec_() - self.updateProfiles() - - def editUserCallback(self, *args): - EditUserCallback(self).exec_() - - def accept(self): - if self.validate(): - super(SettingsDialog, self).accept() - - def cleanup(self, result): - self.config.clearHandlers() - if not result: - self.config.dict = self.configDictCopy - self.config.setCurrentProfile(self.originalCurrentProfile) - if result: - self.parent.remount(self.originalCurrentProfile, self.originalCurrentProfile) - self.parent.updateProfiles() - -class RestoreConfigDialog(QDialog): - """ - Show a dialog that will help to restore BITs configuration. - User can select a config from previous snapshots. - """ - def __init__(self, parent): - super(RestoreConfigDialog, self).__init__(parent) - - self.parent = parent - self.config = parent.config - self.snapshots = parent.snapshots - - import icon - self.icon = icon - self.setWindowIcon(icon.SETTINGS_DIALOG) - self.setWindowTitle(_('Restore Settings')) - - layout = QVBoxLayout(self) - #show a hint on how the snapshot path will look like. - samplePath = os.path.join('backintime', - self.config.host(), - self.config.user(), '1', - snapshots.SID(datetime.datetime.now(), self.config).sid - ) - - #inform user to join group fuse if he hasn't already. - #If there is no group fuse than it is most likly not nessesary. - addFuse = '' - try: - user = self.config.user() - fuse_grp_members = grp.getgrnam('fuse')[3] - if not user in fuse_grp_members: - addFuse = _(' and add your user to group \'fuse\'') - except KeyError: - pass - - label = QLabel(_('Please navigate to the snapshot from which you want ' - 'to restore %(appName)s\'s configuration. The path ' - 'may look like: \n%(samplePath)s\n\n' - 'If your snapshots are on a remote drive or if they are ' - 'encrypted you need to manually mount them first. ' - 'If you use Mode SSH you also may need to set up public key ' - 'login to the remote host%(addFuse)s.\n' - 'Take a look at \'man backintime\'.') - % {'appName': self.config.APP_NAME, 'samplePath': samplePath, - 'addFuse': addFuse}, self) - label.setWordWrap(True) - layout.addWidget(label) - - #treeView - self.treeView = qttools.MyTreeView(self) - self.treeViewModel = QFileSystemModel(self) - self.treeViewModel.setRootPath(QDir().rootPath()) - self.treeViewModel.setReadOnly(True) - self.treeViewModel.setFilter(QDir.AllDirs | - QDir.NoDotAndDotDot | QDir.Hidden) - - self.treeViewFilterProxy = QSortFilterProxyModel(self) - self.treeViewFilterProxy.setDynamicSortFilter(True) - self.treeViewFilterProxy.setSourceModel(self.treeViewModel) - - self.treeViewFilterProxy.setFilterRegExp(r'^[^\.]') - - self.treeView.setModel(self.treeViewFilterProxy) - for col in range(self.treeView.header().count()): - self.treeView.setColumnHidden(col, col != 0) - self.treeView.header().hide() - - #expand users home - self.expandAll(os.path.expanduser('~')) - layout.addWidget(self.treeView) - - #context menu - self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) - self.treeView.customContextMenuRequested.connect(self.onContextMenu) - self.contextMenu = QMenu(self) - self.btnShowHidden = self.contextMenu.addAction(icon.SHOW_HIDDEN, _('Show hidden files')) - self.btnShowHidden.setCheckable(True) - self.btnShowHidden.toggled.connect(self.onBtnShowHidden) - - #colors - self.colorRed = QPalette() - self.colorRed.setColor(QPalette.WindowText, QColor(205, 0, 0)) - self.colorGreen = QPalette() - self.colorGreen.setColor(QPalette.WindowText, QColor(0, 160, 0)) - - #wait indicator which will show that the scan for snapshots is still running - self.wait = QProgressBar(self) - self.wait.setMinimum(0) - self.wait.setMaximum(0) - self.wait.setMaximumHeight(7) - layout.addWidget(self.wait) - - #show where a snapshot with config was found - self.lblFound = QLabel(_('No config found'), self) - self.lblFound.setWordWrap(True) - self.lblFound.setPalette(self.colorRed) - layout.addWidget(self.lblFound) - - #show profiles inside the config - self.widgetProfiles = QWidget(self) - self.widgetProfiles.setContentsMargins(0, 0, 0, 0) - self.widgetProfiles.hide() - self.gridProfiles = QGridLayout() - self.gridProfiles.setContentsMargins(0, 0, 0, 0) - self.gridProfiles.setHorizontalSpacing(20) - self.widgetProfiles.setLayout(self.gridProfiles) - layout.addWidget(self.widgetProfiles) - - self.restoreConfig = None - - self.scan = ScanFileSystem(self) - - self.treeView.myCurrentIndexChanged.connect(self.indexChanged) - self.scan.foundConfig.connect(self.scanFound) - self.scan.finished.connect(self.scanFinished) - - buttonBox = QDialogButtonBox(self) - self.restoreButton = buttonBox.addButton(_('Restore'), QDialogButtonBox.AcceptRole) - self.restoreButton.setEnabled(False) - buttonBox.addButton(QDialogButtonBox.Cancel) - buttonBox.accepted.connect(self.accept) - buttonBox.rejected.connect(self.reject) - layout.addWidget(buttonBox) - - self.scan.start() - - self.resize(600, 700) - - def pathFromIndex(self, index): - """ - return a path string for a given treeView index - """ - sourceIndex = self.treeViewFilterProxy.mapToSource(index) - return str(self.treeViewModel.filePath(sourceIndex)) - - def indexFromPath(self, path): - """ - return the index for path which can be used in treeView - """ - indexSource = self.treeViewModel.index(path) - return self.treeViewFilterProxy.mapFromSource(indexSource) - - def indexChanged(self, current, previous): - """ - called everytime a new item is choosen in treeView. - If there was a config found inside the selected folder, show - available informations about the config. - """ - cfg = self.searchConfig(self.pathFromIndex(current)) - if cfg: - self.expandAll(os.path.dirname(os.path.dirname(cfg._LOCAL_CONFIG_PATH))) - self.lblFound.setText(cfg._LOCAL_CONFIG_PATH) - self.lblFound.setPalette(self.colorGreen) - self.showProfile(cfg) - self.restoreConfig = cfg - else: - self.lblFound.setText(_('No config found')) - self.lblFound.setPalette(self.colorRed) - self.widgetProfiles.hide() - self.restoreConfig = None - self.restoreButton.setEnabled(bool(cfg)) - - def searchConfig(self, path): - """ - try to find config in couple possible subfolders - """ - snapshotPath = os.path.join('backintime', - self.config.host(), - self.config.user()) - tryPaths = ['', '..', 'last_snapshot'] - tryPaths.extend([os.path.join(snapshotPath, str(i), 'last_snapshot') for i in range(10)]) - - for p in tryPaths: - cfgPath = os.path.join(path, p, 'config') - if os.path.exists(cfgPath): - try: - cfg = config.Config(cfgPath) - if cfg.isConfigured(): - return cfg - except: - pass - return - - def expandAll(self, path): - """ - expand all folders from filesystem root to given path - """ - paths = [path, ] - while len(path) > 1: - path = os.path.dirname(path) - paths.append(path) - paths.append('/') - paths.reverse() - [self.treeView.expand(self.indexFromPath(p)) for p in paths] - - def showProfile(self, cfg): - """ - show information about the profiles inside cfg - """ - child = self.gridProfiles.takeAt(0) - while child: - child.widget().deleteLater() - child = self.gridProfiles.takeAt(0) - for row, profileId in enumerate(cfg.profiles()): - for col, txt in enumerate((_('Profile:') + ' ' + str(profileId), - cfg.profileName(profileId), - _('Mode:') + ' ' + cfg.SNAPSHOT_MODES[cfg.snapshotsMode(profileId)][1] - )): - self.gridProfiles.addWidget(QLabel(txt, self), row, col) - self.gridProfiles.setColumnStretch(col, 1) - self.widgetProfiles.show() - - def scanFound(self, path): - """ - scan hit a config. Expand the snapshot folder. - """ - self.expandAll(os.path.dirname(path)) - - def scanFinished(self): - """ - scan is done. Delete the wait indicator - """ - self.wait.deleteLater() - - def onContextMenu(self, point): - self.contextMenu.exec_(self.treeView.mapToGlobal(point)) - - def onBtnShowHidden(self, checked): - if checked: - self.treeViewFilterProxy.setFilterRegExp(r'') - else: - self.treeViewFilterProxy.setFilterRegExp(r'^[^\.]') - - def accept(self): - """ - handle over the dict from the selected config. The dict contains - all settings from the config. - """ - if self.restoreConfig: - self.config.dict = self.restoreConfig.dict - super(RestoreConfigDialog, self).accept() - - def exec_(self): - """ - stop the scan thread if it is still running after dialog was closed. - """ - ret = super(RestoreConfigDialog, self).exec_() - self.scan.stop() - return ret - -class ScanFileSystem(QThread): - CONFIG = 'config' - BACKUP = 'backup' - BACKINTIME = 'backintime' - - foundConfig = pyqtSignal(str) - - def __init__(self, parent): - super(ScanFileSystem, self).__init__(parent) - self.stopper = False - - def stop(self): - """ - prepair stop and wait for finish. - """ - self.stopper = True - return self.wait() - - def run(self): - """ - search in order of hopefully fastest way to find the snapshots. - 1. /home/USER 2. /media 3. /mnt and at last filesystem root. - Already searched paths will be excluded. - """ - searchOrder = [os.path.expanduser('~'), '/media', '/mnt', '/'] - for scan in searchOrder: - exclude = searchOrder[:] - exclude.remove(scan) - for path in self.scanPath(scan, exclude): - self.foundConfig.emit(path) - - def scanPath(self, path, excludes = ()): - """ - walk through all folders and try to find 'config' file. - If found make sure it is nested in backintime/FOO/BAR/1/2345/config and - return its path. - Exclude all paths from excludes and also all backintime/FOO/BAR/1/2345/backup - """ - for root, dirs, files in os.walk(path, topdown = True): - if self.stopper: - return - for exclude in excludes: - exDir, exBase = os.path.split(exclude) - if root == exDir: - if exBase in dirs: - del dirs[dirs.index(exBase)] - if self.CONFIG in files: - rootdirs = root.split(os.sep) - if len(rootdirs) > 4 and rootdirs[-5].startswith(self.BACKINTIME): - if self.BACKUP in dirs: - del dirs[dirs.index(self.BACKUP)] - yield root - -class EditUserCallback(QDialog): - def __init__(self, parent): - super(EditUserCallback, self).__init__(parent) - self.config = parent.config - self.script = self.config.takeSnapshotUserCallback() - - import icon - self.setWindowIcon(icon.SETTINGS_DIALOG) - self.setWindowTitle(self.script) - self.resize(800, 500) - - layout = QVBoxLayout(self) - self.edit = QPlainTextEdit(self) - try: - with open(self.script, 'rt') as f: - self.edit.setPlainText(f.read()) - except IOError: - pass - layout.addWidget(self.edit) - - btnBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent = self) - btnBox.accepted.connect(self.accept) - btnBox.rejected.connect(self.reject) - layout.addWidget(btnBox) - - def checkScript(self, script): - m = re.match(r'^#!(/[\w/-]+)\n', script) - if not m: - logger.error('user-callback script has no shebang (#!/bin/sh) line.') - self.config.errorHandler(_('user-callback script has no shebang (#!/bin/sh) line.')) - return False - if not tools.checkCommand(m.group(1)): - logger.error('Shebang in user-callback script is not executable.') - self.config.errorHandler(_('Shebang in user-callback script is not executable.')) - return False - return True - - def accept(self): - if not self.checkScript(self.edit.toPlainText()): - return - with open(self.script, 'wt') as f: - f.write(self.edit.toPlainText()) - os.chmod(self.script, 0o755) - super(EditUserCallback, self).accept() - -def debugTrace(): - """ - Set a tracepoint in the Python debugger that works with Qt - """ - from pdb import set_trace - pyqtRemoveInputHook() - set_trace() diff --git a/qt/shutdowndlg.py b/qt/shutdowndlg.py new file mode 100644 index 000000000..a964e56ba --- /dev/null +++ b/qt/shutdowndlg.py @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: © 2025 Huaide Jiang +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module about showing a confirmation dialog before shutting down.""" +import gettext +from PyQt6.QtWidgets import (QDialog, + QLabel, + QPushButton, + QVBoxLayout, + QHBoxLayout) +from PyQt6.QtCore import QTimer, Qt + + +class ConfirmShutdownDlg(QDialog): + """A dialog ask for confirmation to shutdown the system and assuming + confirmation after finish a countdown.""" + # pylint: disable=too-few-public-methods + + def __init__(self, countdown: int): + super().__init__() + self.countdown = countdown + + # Initialize UI components + self.setWindowTitle(_('Countdown to Shutdown')) + self.label1 = QLabel(_('The backup has finished.'), self) + self.label2 = QLabel('', self) + self._update_countdown() + + # Center the label texts + self.label1.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.label2.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.cancel_button = QPushButton(_('Cancel Shutdown'), self) + self.shutdown_button = QPushButton(_('Shutdown Now'), self) + self.cancel_button.clicked.connect(self._cancel_shutdown) + + # Immediately accept on shutdown now + self.shutdown_button.clicked.connect(self.accept) + + # Layout setup + layout = QVBoxLayout() + layout.addWidget(self.label1, alignment=Qt.AlignmentFlag.AlignHCenter) + layout.addWidget(self.label2, alignment=Qt.AlignmentFlag.AlignHCenter) + + # Button layout + button_layout = QHBoxLayout() + button_layout.addWidget(self.cancel_button) + button_layout.addWidget(self.shutdown_button) + layout.addLayout(button_layout) + + self.setLayout(layout) + + # Initialize timer + self.timer = QTimer(self) + self.timer.timeout.connect(self._update_countdown) + self.timer.start(1000) + + def _update_countdown(self): + self.countdown -= 1 + + if self.countdown <= 0: + self.timer.stop() + self.accept() + + self.label2.setText(gettext.ngettext( + 'The system will shut down in {n} second.', + 'The system will shut down in {n} seconds.', + self.countdown).format(n=self.countdown)) + + def _cancel_shutdown(self): + self.timer.stop() + self.reject() + + +def get_shutdown_confirmation(countdown: int = 31) -> bool: + """Ask user to confirm the shutdown while running a countdown. + + Args: + countdown: Countdown in seconds. + + Returns: + Confirm shutdown if user has not answered, or users answer. + """ + dialog = ConfirmShutdownDlg(countdown) + result = dialog.exec() + return result == QDialog.DialogCode.Accepted diff --git a/qt/snapshotsdialog.py b/qt/snapshotsdialog.py index 8e0b956d1..af2da869b 100644 --- a/qt/snapshotsdialog.py +++ b/qt/snapshotsdialog.py @@ -1,90 +1,128 @@ -# Back In Time -# Copyright (C) 2008-2021 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-2.0-or-later # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Stuff around the snapshots dialog. +That dialog will be removed and its functionality integrated into the main +window and its timeline widget.""" import os -import gettext import subprocess import shlex -from PyQt5.QtGui import * -from PyQt5.QtWidgets import * -from PyQt5.QtCore import * - +# from PyQt6.QtGui import QDesktopServices +from PyQt6.QtWidgets import (QCheckBox, + QDialog, + QDialogButtonBox, + QGridLayout, + QHBoxLayout, + QLabel, + QLineEdit, + QMenu, + QPushButton, + QToolBar, + QVBoxLayout) +from PyQt6.QtCore import Qt, QThread + +from timeline import TimeLine +from bitwidgets import SnapshotCombo import tools import restoredialog import messagebox -import qttools import snapshots +import logger +from inhibitsuspend import InhibitSuspend -_=gettext.gettext +DIFF_PARAMS = '%1 %2' if tools.checkCommand('meld'): DIFF_CMD = 'meld' - DIFF_PARAMS = '%1 %2' elif tools.checkCommand('kompare'): DIFF_CMD = 'kompare' - DIFF_PARAMS = '%1 %2' else: - DIFF_CMD = 'false' - DIFF_PARAMS = '%1 %2' + DIFF_CMD = '' + class DiffOptionsDialog(QDialog): + """Dialog to setup diff options""" + def __init__(self, parent): super(DiffOptionsDialog, self).__init__(parent) self.config = parent.config import icon self.setWindowIcon(icon.DIFF_OPTIONS) - self.setWindowTitle(_('Diff Options')) + self.setWindowTitle(_('Options about comparing backups')) self.mainLayout = QGridLayout(self) - self.diffCmd = self.config.strValue('qt.diff.cmd', DIFF_CMD) - self.diffParams = self.config.strValue('qt.diff.params', DIFF_PARAMS) + cmd = self.config.strValue('qt.diff.cmd', DIFF_CMD) + params = self.config.strValue('qt.diff.params', DIFF_PARAMS) self.mainLayout.addWidget(QLabel(_('Command:')), 0, 0) - self.editCmd = QLineEdit(self.diffCmd, self) + self.editCmd = QLineEdit(cmd, self) self.mainLayout.addWidget(self.editCmd, 0, 1) self.mainLayout.addWidget(QLabel(_('Parameters:')), 1, 0) - self.editParams = QLineEdit(self.diffParams, self) + self.editParams = QLineEdit(params, self) self.mainLayout.addWidget(self.editParams, 1, 1) - self.mainLayout.addWidget(QLabel(_('Use %1 and %2 for path parameters')), 2, 1) + self.mainLayout.addWidget( + QLabel(_('Use %1 and %2 for path parameters')), 2, 1) - buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok + | QDialogButtonBox.StandardButton.Cancel) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) self.mainLayout.addWidget(buttonBox, 3, 0, 3, 2) def accept(self): - diffCmd = str(self.editCmd.text().toUtf8()) - diffParams = str(self.editParams.text().toUtf8()) + """OK was clicked""" + + # Get values from text dialogs fields + cmd = self.editCmd.text() + params = self.editParams.text() + + # Any value? + if not cmd: + messagebox.info(_('Please set a diff command or press Cancel.')) + return + + # Command exists? + if not tools.checkCommand(cmd): + messagebox.info(_( + 'The command "{cmd}" cannot be found on this system. Please ' + 'try something else or press Cancel.').format(cmd=cmd)) + return + + if not params: + params = DIFF_PARAMS + messagebox.critical( + self, + _('No parameters set for the diff command. Using ' + 'default value "{params}".').format(params=params)) - if diffCmd != self.diffCmd or diffParams != self.diffParams: - self.config.setStrValue('qt.diff.cmd', diffCmd) - self.config.setStrValue('qt.diff.params', diffParams) - self.config.save() + # save new values + self.config.setStrValue('qt.diff.cmd', cmd) + self.config.setStrValue('qt.diff.params', params) + self.config.save() super(DiffOptionsDialog, self).accept() class SnapshotsDialog(QDialog): + """The main snapshots dialog + + Dev note (buhtz, 2025-07-29): Scheduled to removed and replaced be features + in the main window. + """ + def __init__(self, parent, sid, path): super(SnapshotsDialog, self).__init__(parent) self.parent = parent @@ -98,84 +136,102 @@ def __init__(self, parent, sid, path): self.path = path self.setWindowIcon(icon.SNAPSHOTS) - self.setWindowTitle(_('Snapshots')) + self.setWindowTitle(_('Backups')) self.mainLayout = QVBoxLayout(self) - #path + # path self.editPath = QLineEdit(self.path, self) self.editPath.setReadOnly(True) self.mainLayout.addWidget(self.editPath) - #list different snapshots only - self.cbOnlyDifferentSnapshots = QCheckBox(_('List only different snapshots'), self) + # list different snapshots only + self.cbOnlyDifferentSnapshots = QCheckBox( + _('Differing backups only'), self) self.mainLayout.addWidget(self.cbOnlyDifferentSnapshots) - self.cbOnlyDifferentSnapshots.stateChanged.connect(self.cbOnlyDifferentSnapshotsChanged) + self.cbOnlyDifferentSnapshots.stateChanged.connect( + self.cbOnlyDifferentSnapshotsChanged) - #list equal snapshots only + # list equal snapshots only layout = QHBoxLayout() self.mainLayout.addLayout(layout) - self.cbOnlyEqualSnapshots = QCheckBox(_('List only equal snapshots to: '), self) - self.cbOnlyEqualSnapshots.stateChanged.connect(self.cbOnlyEqualSnapshotsChanged) + self.cbOnlyEqualSnapshots = QCheckBox( + _('List only backups that are equal to:'), self) + self.cbOnlyEqualSnapshots.stateChanged.connect( + self.cbOnlyEqualSnapshotsChanged) layout.addWidget(self.cbOnlyEqualSnapshots) - self.comboEqualTo = qttools.SnapshotCombo(self) + self.comboEqualTo = SnapshotCombo(self) self.comboEqualTo.currentIndexChanged.connect(self.comboEqualToChanged) self.comboEqualTo.setEnabled(False) layout.addWidget(self.comboEqualTo) - #deep check - self.cbDeepCheck = QCheckBox(_('Deep check (more accurate, but slow)'), self) + # deep check + self.cbDeepCheck = QCheckBox( + _('Deep check (more accurate, but slow)'), self) self.mainLayout.addWidget(self.cbDeepCheck) self.cbDeepCheck.stateChanged.connect(self.cbDeepCheckChanged) - #toolbar + # toolbar self.toolbar = QToolBar(self) self.toolbar.setFloatable(False) self.mainLayout.addWidget(self.toolbar) - #toolbar restore + # toolbar restore menuRestore = QMenu(self) action = menuRestore.addAction(icon.RESTORE, _('Restore')) action.triggered.connect(self.restoreThis) - action = menuRestore.addAction(icon.RESTORE_TO, _('Restore to ...')) + action = menuRestore.addAction(icon.RESTORE_TO, _('Restore to …')) action.triggered.connect(self.restoreThisTo) self.btnRestore = self.toolbar.addAction(icon.RESTORE, _('Restore')) self.btnRestore.setMenu(menuRestore) self.btnRestore.triggered.connect(self.restoreThis) - #btn delete + # btn delete self.btnDelete = self.toolbar.addAction(icon.DELETE_FILE, _('Delete')) self.btnDelete.triggered.connect(self.btnDeleteClicked) - #btn select_all - self.btnSelectAll = self.toolbar.addAction(icon.SELECT_ALL, _('Select All')) + # btn select_all + self.btnSelectAll = self.toolbar.addAction( + icon.SELECT_ALL, _('Select All')) self.btnSelectAll.triggered.connect(self.btnSelectAllClicked) - #snapshots list - self.timeLine = qttools.TimeLine(self) - self.mainLayout.addWidget(self.timeLine) - self.timeLine.itemSelectionChanged.connect(self.timeLineChanged) - self.timeLine.itemActivated.connect(self.timeLineExecute) + # snapshots list + self.timeline = TimeLine(self) - #diff + self.timeline.event_now_selected.register([ + self._on_now_selected, + ]) + self.timeline.event_backup_selected.register([ + self._on_backup_selected, + ]) + + self.mainLayout.addWidget(self.timeline) + # self.timeLine.itemActivated.connect(self.timeLineExecute) + + # Diff layout = QHBoxLayout() self.mainLayout.addLayout(layout) - self.btnDiff = QPushButton(_('Diff'), self) + self.btnDiff = QPushButton(_('Compare'), self) layout.addWidget(self.btnDiff) self.btnDiff.clicked.connect(self.btnDiffClicked) + self._update_btn_diff() - self.comboDiff = qttools.SnapshotCombo(self) + self.comboDiff = SnapshotCombo(self) layout.addWidget(self.comboDiff, 2) - #buttons - buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) - self.btnGoto = buttonBox.button(QDialogButtonBox.Ok) - self.btnCancel = buttonBox.button(QDialogButtonBox.Cancel) + # buttons + buttonBox = QDialogButtonBox( + QDialogButtonBox.StandardButton.Ok + | QDialogButtonBox.StandardButton.Cancel) + self.btnGoto = buttonBox.button(QDialogButtonBox.StandardButton.Ok) + self.btnCancel = buttonBox.button( + QDialogButtonBox.StandardButton.Cancel) self.btnGoto.setText(_('Go To')) - btnDiffOptions = buttonBox.addButton(_('Diff Options'), QDialogButtonBox.HelpRole) + btnDiffOptions = buttonBox.addButton( + _('Options'), QDialogButtonBox.ButtonRole.HelpRole) btnDiffOptions.setIcon(icon.DIFF_OPTIONS) self.mainLayout.addWidget(buttonBox) @@ -184,7 +240,6 @@ def __init__(self, parent, sid, path): buttonBox.rejected.connect(self.reject) btnDiffOptions.clicked.connect(self.btnDiffOptionsClicked) - # self.cbDeepCheck.setEnabled(False) full_path = self.sid.pathBackup(self.path) @@ -196,33 +251,52 @@ def __init__(self, parent, sid, path): self.comboEqualTo.hide() self.cbDeepCheck.hide() - #update list and combobox + # update list and combobox self.UpdateSnapshotsAndComboEqualTo() - def addSnapshot(self, sid): - self.timeLine.addSnapshot(sid) + def _on_now_selected(self): + self.timeLineChanged() - #add to combo - self.comboDiff.addSnapshotID(sid) + def _on_backup_selected(self, _sid): + self.timeLineChanged() + + def addSnapshot(self, sid): + """Add backup (sid) in the timeine widget""" + # pylint: disable-next=duplicate-code + self.timeline.create_backup_entry( + descriptor=sid.get_descriptor(), + timestamp=sid.get_timestamp(), + last_checked=sid.lastChecked, + label=sid.displayName + ) + + # to combo box + self.comboDiff.add_snapshot_id(sid) if self.sid == sid: - self.comboDiff.setCurrentSnapshotID(sid) - self.comboDiff.checkSelection() + self.comboDiff.set_current_snapshot_id(sid) + self.comboDiff.check_selection() def updateSnapshots(self): - self.timeLine.clear() + self.timeline.clear_and_reset() self.comboDiff.clear() - equal_to_sid = self.comboEqualTo.currentSnapshotID() + equal_to_sid = self.comboEqualTo.current_snapshot_id() + if self.cbOnlyEqualSnapshots.isChecked() and equal_to_sid: equal_to = equal_to_sid.pathBackup(self.path) else: equal_to = False - snapshotsFiltered = self.snapshots.filter(self.sid, self.path, - self.snapshotsList, - self.cbOnlyDifferentSnapshots.isChecked(), - self.cbDeepCheck.isChecked(), - equal_to) + + snapshotsFiltered = self.snapshots.filter( + base_sid=self.sid, + base_path=self.path, # !!! + snapshotsList=self.snapshotsList, + list_diff_only=self.cbOnlyDifferentSnapshots.isChecked(), + flag_deep_check=self.cbDeepCheck.isChecked(), + list_equal_to=equal_to + ) + for sid in snapshotsFiltered: self.addSnapshot(sid) @@ -230,14 +304,16 @@ def updateSnapshots(self): def UpdateComboEqualTo(self): self.comboEqualTo.clear() - snapshotsFiltered = self.snapshots.filter(self.sid, self.path, self.snapshotsList) + snapshotsFiltered = self.snapshots.filter( + self.sid, self.path, self.snapshotsList) + for sid in snapshotsFiltered: - self.comboEqualTo.addSnapshotID(sid) + self.comboEqualTo.add_snapshot_id(sid) if sid == self.sid: - self.comboEqualTo.setCurrentSnapshotID(sid) + self.comboEqualTo.set_current_snapshot_id(sid) - self.comboEqualTo.checkSelection() + self.comboEqualTo.check_selection() def UpdateSnapshotsAndComboEqualTo(self): self.updateSnapshots() @@ -262,17 +338,24 @@ def cbDeepCheckChanged(self): self.updateSnapshots() def updateToolbar(self): - sids = self.timeLine.selectedSnapshotIDs() + sids = self.timeline.get_all_selected_backup_descriptors() + sids = [ + snapshots.SID(date=descriptor, cfg=self.config) + for descriptor in sids + ] if not sids: enable_restore = False enable_delete = False + elif len(sids) == 1: enable_restore = not sids[0].isRoot - enable_delete = not sids[0].isRoot + enable_delete = not sids[0].isRoot + else: enable_restore = False enable_delete = True + for sid in sids: if sid.isRoot: enable_delete = False @@ -281,92 +364,123 @@ def updateToolbar(self): self.btnDelete.setEnabled(enable_delete) def restoreThis(self): - sid = self.timeLine.currentSnapshotID() + # See #1485 as related bug report + sid = self.timeline.current_snapshot_id() if not sid.isRoot: + # pylint: disable-next=E1101 restoredialog.restore(self, sid, self.path) def restoreThisTo(self): - sid = self.timeLine.currentSnapshotID() + # See #1485 as related bug report + sid = self.timeline.current_snapshot_id() if not sid.isRoot: + # pylint: disable-next=E1101 restoredialog.restore(self, sid, self.path, None) def timeLineChanged(self): self.updateToolbar() - def timeLineExecute(self, item, column): - if self.qapp.keyboardModifiers() and Qt.ControlModifier: - return + # def _deprecated_timeLineExecute(self, _item, _column): + # # Ctrl button pressed, indicates ongoing multiselection? + # modifiers = self.qapp.keyboardModifiers() + # if Qt.KeyboardModifier.ControlModifier in modifiers: + # return - sid = self.timeLine.currentSnapshotID() - if not sid: - return + # sid = self.timeline.current_snapshot_id() + # if not sid: + # return - full_path = sid.pathBackup(self.path) - if not os.path.exists(full_path): - return + # full_path = sid.pathBackup(self.path) + # if not os.path.exists(full_path): + # return - # prevent backup data from being accidentally overwritten - # by create a temporary local copy and only open that one - if not isinstance(self.sid, snapshots.RootSnapshot): - full_path = self.parent.tmpCopy(full_path, sid) + # # prevent backup data from being accidentally overwritten + # # by create a temporary local copy and only open that one + # if not isinstance(self.sid, snapshots.RootSnapshot): + # full_path = self.parent._create_temporary_copy(full_path, sid) - self.run = QDesktopServices.openUrl(QUrl(full_path)) + # QDesktopServices.openUrl(QUrl(full_path)) def btnDiffClicked(self): - sid1 = self.timeLine.currentSnapshotID() - sid2 = self.comboDiff.currentSnapshotID() + sid1 = None + if self.timeline.is_now_selected(): + sid1 = snapshots.RootSnapshot(self.config) + else: + backup_descriptor = self.timeline.selected_backup_descriptor() + if backup_descriptor: + sid1 = snapshots.SID(backup_descriptor, self.config) + + sid2 = self.comboDiff.current_snapshot_id() + if not sid1 or not sid2: return path1 = sid1.pathBackup(self.path) path2 = sid2.pathBackup(self.path) - #check if the 2 paths are different + # check if the 2 paths are different if path1 == path2: - messagebox.critical(self, _('You can\'t compare a snapshot to itself')) + messagebox.critical( + self, _('It is not possible to compare a backup to ' + 'itself, as the comparison would be redundant.') + ) return diffCmd = self.config.strValue('qt.diff.cmd', DIFF_CMD) diffParams = self.config.strValue('qt.diff.params', DIFF_PARAMS) - if not tools.checkCommand(diffCmd): - messagebox.critical(self, _('Command not found: %s') % diffCmd) - return - # prevent backup data from being accidentally overwritten # by create a temporary local copy and only open that one if not isinstance(sid1, snapshots.RootSnapshot): - path1 = self.parent.tmpCopy(path1, sid1) + path1 = self.parent._create_temporary_copy(path1, sid1) if not isinstance(sid2, snapshots.RootSnapshot): - path2 = self.parent.tmpCopy(path2, sid2) + path2 = self.parent._create_temporary_copy(path2, sid2) params = diffParams - params = params.replace('%1', '"%s"' %path1) - params = params.replace('%2', '"%s"' %path2) + params = params.replace('%1', '"%s"' % path1) + params = params.replace('%2', '"%s"' % path2) cmd = diffCmd + ' ' + params + + logger.debug(f'Compare two backups with command {cmd}.') + subprocess.Popen(shlex.split(cmd)) + def _update_btn_diff(self): + """Enable the Compare button if diff command is set otherwise Disable + it.""" + cmd = self.config.strValue('qt.diff.cmd', DIFF_CMD) + self.btnDiff.setDisabled(not cmd) + def btnDiffOptionsClicked(self): - DiffOptionsDialog(self).exec_() + DiffOptionsDialog(self).exec() + self._update_btn_diff() - def comboEqualToChanged(self, index): + def comboEqualToChanged(self, _index): self.updateSnapshots() def btnDeleteClicked(self): - items = self.timeLine.selectedItems() + items = self.timeline.selectedItems() + if not items: return - elif len(items) == 1: - msg = _('Do you really want to delete "%(file)s" in snapshot "%(snapshot_id)s?\n') \ - % {'file' : self.path, 'snapshot_id' : items[0].snapshotID()} + + if len(items) == 1: + msg = _( + 'Really delete {file_or_dir} in backup {backup_id}?').format( + file_or_dir=f'"{self.path}"', + backup_id=f'"{items[0].snapshot_id}"') + else: - msg = _('Do you really want to delete "%(file)s" in %(count)d snapshots?\n') \ - % {'file' : self.path, 'count' : len(items)} - msg += _('WARNING: This can not be revoked!') - if QMessageBox.Yes == messagebox.warningYesNo(self, msg): + msg = _('Really delete {file_or_dir} in {count} backups?').format( + file_or_dir=f'"{self.path}"', count=len(items)) + + msg = msg + '\n' + _('WARNING: This cannot be revoked.') + + if messagebox.question(msg): + for item in items: - item.setFlags(Qt.NoItemFlags) + item.setFlags(Qt.ItemFlag.NoItemFlags) thread = RemoveFileThread(self, items) thread.started.connect(lambda: self.btnGoto.setDisabled(True)) @@ -378,51 +492,49 @@ def btnDeleteClicked(self): thread.start() exclude = self.config.exclude() - msg = _('Exclude "%s" from future snapshots?' % self.path) - if self.path not in exclude and QMessageBox.Yes == messagebox.warningYesNo(self, msg): - exclude.append(self.path) - self.config.setExclude(exclude) + msg = _('Exclude {path} from future backups?').format( + path=f'"{self.path}"') + + if self.path not in exclude: + + if messagebox.question(msg): + exclude.append(self.path) + self.config.setExclude(exclude) def btnSelectAllClicked(self): - """ - select all expect 'Now' - """ - self.timeLine.clearSelection() - for item in self.timeLine.iterSnapshotItems(): - if not isinstance(item.snapshotID(), snapshots.RootSnapshot): - item.setSelected(True) + """Select all backups but not 'Now'""" + self.timeline.select_all_backup_entries() def accept(self): - sid = self.timeLine.currentSnapshotID() + sid = self.timeline.selected_backup_descriptor() if sid: - self.sid = sid + self.sid = snapshots.SID(sid, self.config) super(SnapshotsDialog, self).accept() + class RemoveFileThread(QThread): """ remove files in background thread so GUI will not freeze """ + def __init__(self, parent, items): self.parent = parent - self.config = parent.config + # self.config = parent.config self.snapshots = parent.snapshots self.items = items super(RemoveFileThread, self).__init__(parent) def run(self): - #inhibit suspend/hibernate during delete - self.config.inhibitCookie = tools.inhibitSuspend(toplevel_xid = self.config.xWindowId, - reason = 'deleting files') - - for item in self.items: - self.snapshots.deletePath(item.snapshotID(), self.parent.path) - try: - item.setHidden(True) - except RuntimeError: - #item has been deleted - #probably because user refreshed treeview - pass - - #release inhibit suspend - if self.config.inhibitCookie: - self.config.inhibitCookie = tools.unInhibitSuspend(*self.config.inhibitCookie) + with InhibitSuspend(reason='deleting files'): + + for item in self.items: + + self.snapshots.deletePath(item.snapshot_id, self.parent.path) + + try: + item.setHidden(True) + + except RuntimeError: + # item has been deleted + # probably because user refreshed treeview + pass diff --git a/qt/statedata.py b/qt/statedata.py new file mode 100644 index 000000000..03d4536c3 --- /dev/null +++ b/qt/statedata.py @@ -0,0 +1,457 @@ +# SPDX-FileCopyrightText: © 2024 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Management of the state file.""" +# pylint: disable=wrong-import-position,wrong-import-order +from __future__ import annotations +import os +import json +from pathlib import Path +from datetime import datetime, timezone +from copy import deepcopy +from qttools_path import register_backintime_path +register_backintime_path('common') +import singleton # noqa: E402 +import logger # noqa: E402 +import tools # noqa: E402 +from version import __version__ # noqa: E402 + + +# pylint: disable-next=too-many-public-methods +class StateData(dict, metaclass=singleton.Singleton): + """Manage state data for Back In Time. + + Dev note (buhtz, 2024-12): It is usually recommended and preferred to + derive from `collections.UserDict` instead of just `dict`. But this + conflicts with the ``metaclass=``. To my current knowledge this is not a + big deal and won't introduce any problems. + + """ + # pylint: disable=too-many-instance-attributes + # The default structure. All properties do rely on them and assuming + # it is there. + _EMPTY_STRUCT = { + 'gui': { + 'mainwindow': { + 'files_view': {}, + 'last_path': {}, + 'places_sorting': {}, + }, + 'manage_profiles': { + 'incl_sorting': {}, + 'excl_sorting': {}, + 'dims': {}, + }, + 'logview': {}, + 'user_callback_edit': {}, + }, + 'message': { + 'encfs': {} + }, + } + + class Profile: + """A surrogate to access profile-specific state data.""" + + def __init__(self, profile_id: str, state: StateData): + self._state = state + self._profile_id = profile_id + + @property + def msg_encfs(self) -> int: + """Stage of EncFS deprecation warning shown as last.""" + try: + return self._state['message']['encfs'][self._profile_id] + except KeyError: + self.msg_encfs = 0 + return self.msg_encfs + + @msg_encfs.setter + def msg_encfs(self, val: int) -> None: + self._state['message']['encfs'][self._profile_id] = val + + @property + def last_path(self) -> Path: + """Last path used in the GUI. + + Raises: + KeyError + """ + return Path(self._state['gui']['mainwindow'][ + 'last_path'][self._profile_id]) + + @last_path.setter + def last_path(self, path: Path) -> None: + self._state['gui']['mainwindow'][ + 'last_path'][self._profile_id] = str(path) + + @property + def places_sorting(self) -> tuple[int, int]: + """Column index and sort order. + + Returns: + Tuple with column index and its sorting order (0=ascending). + """ + return self._state['gui']['mainwindow'][ + 'places_sorting'][self._profile_id] + + @places_sorting.setter + def places_sorting(self, vals: tuple[int, int]) -> None: + self._state['gui']['mainwindow'][ + 'places_sorting'][self._profile_id] = vals + + @property + def exclude_sorting(self) -> tuple[int, int]: + """Column index and sort order. + + Returns: + Tuple with column index and its sorting order (0=ascending). + """ + return self._state['gui']['manage_profiles'][ + 'excl_sorting'][self._profile_id] + + @exclude_sorting.setter + def exclude_sorting(self, vals: tuple[int, int]) -> None: + self._state['gui']['manage_profiles'][ + 'excl_sorting'][self._profile_id] = vals + + @property + def include_sorting(self) -> tuple[int, int]: + """Column index and sort order. + + Returns: + Tuple with column index and its sorting order (0=ascending). + """ + return self._state['gui']['manage_profiles'][ + 'incl_sorting'][self._profile_id] + + @include_sorting.setter + def include_sorting(self, vals: tuple[int, int]) -> None: + self._state['gui']['manage_profiles'][ + 'incl_sorting'][self._profile_id] = vals + + @staticmethod + def file_path() -> Path: + """Returns the state file path.""" + xdg_state = os.environ.get('XDG_STATE_HOME', None) + if xdg_state: + xdg_state = Path(xdg_state) + else: + xdg_state = Path.home() / '.local' / 'state' + + fp = xdg_state / 'backintime-qt.json' + + logger.debug(f'State file path: {fp}') + + return fp + + def __init__(self, data: dict = None): + """Constructor.""" + + # default + full = deepcopy(self._EMPTY_STRUCT) + + if data: + full = tools.nested_dict_update(full, data) + + super().__init__(full) + + def __str__(self): + return json.dumps(self, indent=4) + + def _set_save_meta_data(self): + meta = { + 'saved': datetime.now().isoformat(), + 'saved_utc': datetime.now(timezone.utc).isoformat(), + 'bitversion': __version__, + } + + self['_meta'] = meta + + def save(self): + """Store application state data to a file.""" + logger.debug('Save state data.') + + self._set_save_meta_data() + + fp = self.file_path() + fp.parent.mkdir(parents=True, exist_ok=True) + + with fp.open('w', encoding='utf-8') as handle: + handle.write(str(self)) + + def profile(self, profile_id: str) -> StateData.Profile: + """Return a `Profile` object related to the given id. + + Args: + profile_id: A profile_id of a snapshot profile. + + Returns: + A profile surrogate. + + Raises: + KeyError: If profile does not exists. + """ + return StateData.Profile(profile_id=profile_id, state=self) + + def manual_starts_countdown(self) -> int: + """Countdown value about how often the users started the Back In Time + GUI. + + At the end of the countown the `ApproachTranslatorDialog` is presented + to the user. + """ + return self.get('manual_starts_countdown', 10) + + def decrement_manual_starts_countdown(self): + """Counts down to -1. + + See :py:func:`manual_starts_countdown()` for details. + """ + val = self.manual_starts_countdown() + + if val > -1: + self['manual_starts_countdown'] = val - 1 + + @property + def msg_release_candidate(self) -> str: + """Last version of Back In Time in which the release candidate message + box was displayed. + """ + try: + return self['message']['release_candidate'] + except KeyError: + self.msg_release_candidate = None + return self.msg_release_candidate + + @msg_release_candidate.setter + def msg_release_candidate(self, val: str) -> None: + self['message']['release_candidate'] = val + + @property + def msg_language_remove(self) -> bool: + """Language planned for removal message shown.""" + try: + return self['message']['language_remove'] + except KeyError: + self.msg_language_remove = False + return self.msg_language_remove + + @msg_language_remove.setter + def msg_language_remove(self, val: bool) -> None: + self['message']['language_remove'] = val + + @property + def msg_cipher_deprecation(self) -> bool: + """Cipher deprecation message shown.""" + try: + return self['message']['cipher_deprecation'] + except KeyError: + self.msg_cipher_deprecation = False + return self.msg_cipher_deprecation + + @msg_cipher_deprecation.setter + def msg_cipher_deprecation(self, val: bool) -> None: + self['message']['cipher_deprecation'] = val + + @property + def msg_encfs_global(self) -> int: + """Last stage of global EncFS deprecation message that was shown.""" + try: + return self['message']['encfs']['global'] + except KeyError: + self.msg_encfs_global = 0 + return self.msg_encfs_global + + @msg_encfs_global.setter + def msg_encfs_global(self, val: int) -> None: + self['message']['encfs']['global'] = val + + @property + def mainwindow_show_hidden(self) -> bool: + """Show hidden files in files view.""" + try: + return self['gui']['mainwindow']['show_hidden'] + except KeyError: + self.mainwindow_show_hidden = False + return self.mainwindow_show_hidden + + @mainwindow_show_hidden.setter + def mainwindow_show_hidden(self, val: bool) -> None: + self['gui']['mainwindow']['show_hidden'] = val + + @property + def mainwindow_maximized(self) -> bool: + """Main window maximized state""" + return self.mainwindow_dims == [-1, -1] + + def set_mainwindow_maximized(self): + """Main window is maximized state""" + self.mainwindow_dims = [-1, -1] + + @property + def mainwindow_dims(self) -> tuple[int, int]: + """Dimensions of the main window. + + Raises: + KeyError + """ + return self['gui']['mainwindow']['dims'] + + @mainwindow_dims.setter + def mainwindow_dims(self, vals: tuple[int, int]) -> None: + self['gui']['mainwindow']['dims'] = vals + + @property + def mainwindow_coords(self) -> tuple[int, int]: + """Coordinates (position) of the main window. + + Raises: + KeyError + """ + return self['gui']['mainwindow']['coords'] + + @mainwindow_coords.setter + def mainwindow_coords(self, vals: tuple[int, int]) -> None: + self['gui']['mainwindow']['coords'] = vals + + @property + def logview_dims(self) -> tuple[int, int]: + """Dimensions of the log view dialog. + + Raises: + KeyError + """ + try: + return self['gui']['logview']['dims'] + except KeyError: + self.logview_dims = (800, 500) + return self.logview_dims + + @logview_dims.setter + def logview_dims(self, vals: tuple[int, int]) -> None: + self['gui']['logview']['dims'] = vals + + @property + def files_view_sorting(self) -> tuple[int, int]: + """Column index and sort order. + + Returns: + Tuple with column index and its sorting order (0=ascending). + """ + try: + return self['gui']['mainwindow']['files_view']['sorting'] + except KeyError: + self.files_view_sorting = (0, 0) + return self.files_view_sorting + + @files_view_sorting.setter + def files_view_sorting(self, vals: tuple[int, int]) -> None: + self['gui']['mainwindow']['files_view']['sorting'] = vals + + @property + def files_view_col_widths(self) -> tuple: + """Widths of columns in the files view.""" + return self['gui']['mainwindow']['files_view']['col_widths'] + + @files_view_col_widths.setter + def files_view_col_widths(self, widths: tuple) -> None: + self['gui']['mainwindow']['files_view']['col_widths'] = widths + + @property + def mainwindow_main_splitter_widths(self) -> tuple[int, int]: + """Left and right width of main splitter in main window. + + Returns: + Two entry tuple with right and left widths. + """ + try: + return self['gui']['mainwindow']['splitter_main_widths'] + except KeyError: + self.mainwindow_main_splitter_widths = (150, 450) + return self.mainwindow_main_splitter_widths + + @mainwindow_main_splitter_widths.setter + def mainwindow_main_splitter_widths(self, vals: tuple[int, int]) -> None: + self['gui']['mainwindow']['splitter_main_widths'] = vals + + @property + def mainwindow_second_splitter_widths(self) -> tuple[int, int]: + """Left and right width of second splitter in main window. + + Returns: + Two entry tuple with right and left widths. + """ + try: + return self['gui']['mainwindow']['splitter_second_widths'] + except KeyError: + self.mainwindow_second_splitter_widths = (150, 300) + return self.mainwindow_second_splitter_widths + + @mainwindow_second_splitter_widths.setter + def mainwindow_second_splitter_widths(self, vals: tuple[int, int]) -> None: + self['gui']['mainwindow']['splitter_second_widths'] = vals + + @property + def toolbar_button_style(self) -> int: + """Style of icons for the main toolbar. + + Returns: + Style value as integer (default: 0 as ``ToolButtonIconOnly``) + """ + try: + return self['gui']['mainwindow']['toolbar_button_style'] + except KeyError: + self.toolbar_button_style = 0 + return self.toolbar_button_style + + @toolbar_button_style.setter + def toolbar_button_style(self, value) -> None: + self['gui']['mainwindow']['toolbar_button_style'] = value + + def get_manageprofiles_dims_coords(self, profile_mode: str + ) -> tuple[tuple[int, int], + tuple[int, int]]: + """Dimension and coordinates of the Manage Profiles dialog window""" + return ( + self['gui']['manage_profiles']['dims'][profile_mode], + self['gui']['manage_profiles']['coords'] + ) + + def set_manageprofiles_dims_coords(self, + profile_mode: str, + dims: tuple[int, int], + coords: tuple[int, int]): + """Dimension and coordinates of the Manage Profiles dialog window""" + self['gui']['manage_profiles']['dims'][profile_mode] = dims + self['gui']['manage_profiles']['coords'] = coords + + @property + def user_callback_edit_dims(self) -> tuple[int, int]: + """Dimensions of the user-callback edit dialog. + + Raises: + KeyError + """ + return self['gui']['user_callback_edit']['dims'] + + @user_callback_edit_dims.setter + def user_callback_edit_dims(self, vals: tuple[int, int]) -> None: + self['gui']['user_callback_edit']['dims'] = vals + + @property + def user_callback_edit_coords(self) -> tuple[int, int]: + """Coordinates (position) of the user-callback edit dialog. + + Raises: + KeyError + """ + return self['gui']['user_callback_edit']['coords'] + + @user_callback_edit_coords.setter + def user_callback_edit_coords(self, vals: tuple[int, int]) -> None: + self['gui']['user_callback_edit']['coords'] = vals diff --git a/qt/statusbar.py b/qt/statusbar.py new file mode 100644 index 000000000..3bc70e60b --- /dev/null +++ b/qt/statusbar.py @@ -0,0 +1,127 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""A module offering a status bar widget +""" +from PyQt6.QtWidgets import (QFrame, + QHBoxLayout, + QLabel, + QMainWindow, + QProgressBar, + QSizePolicy, + QStatusBar, + QWidget, + ) +from PyQt6.QtCore import QEvent +from PyQt6.QtGui import QPalette, QColor +import bitbase +import qttools + +_PROGRESS_BAR_WIDTH_FX = 10 + + +class StatusBar(QStatusBar): + """A status bar widget""" + + def __init__(self, main_window: QMainWindow): + super().__init__(parent=main_window) + + self.main_window = main_window + + # Root mode indicator + self._root = self._root_mode_indicator() + + # A container widget give us more control about layout details + container = QWidget(self) + layout = QHBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + container.setLayout(layout) + + # Status text + self._status = QLabel(container) + self._status.setWordWrap(False) + self._status.setSizePolicy( + QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + + # Progress bar + self._progress = QProgressBar(container) + self._progress.setRange(0, 100) + self._progress.setValue(0) + self._progress.setTextVisible(False) + self._progress.setVisible(False) + + # Layout + if self._root: + layout.addWidget(self._root) + layout.addWidget(self._status, stretch=_PROGRESS_BAR_WIDTH_FX-1) + layout.addStretch(0) + layout.addWidget(self._progress, stretch=1) + self.addPermanentWidget(container, 1) + container.resizeEvent = self._on_resize + + def _on_resize(self, event: QEvent) -> None: + """Set the status label with in pixels, but relative. + + The width is a fraction of the statusbar full width, considering the + width of the progressbar, which is also defined by a fraction. + """ + width = self._status.parentWidget().width() + width = width * (1 - (1 / _PROGRESS_BAR_WIDTH_FX)) + self._status.setMaximumWidth(int(width)) + event.accept() + + def _root_mode_indicator(self) -> QLabel: + if not bitbase.IS_IN_ROOT_MODE: + return None + + root = QLabel(_('Root mode')) + root.setToolTip(_( + 'Back In Time is currently running with root ' + 'privileges (full system access)')) + root.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken) + + font = root.font() + font.setBold(True) + root.setFont(font) + + if qttools.in_dark_mode(root): + # dark red & white + bg_color = '#aa0000' + text_color = '#ffffff' + else: + # light pink & dark red + bg_color = '#ffdddd' + text_color = '#aa0000' + + palette = root.palette() + palette.setColor(QPalette.ColorRole.Window, QColor(bg_color)) + palette.setColor(QPalette.ColorRole.WindowText, QColor(text_color)) + + root.setAutoFillBackground(True) + root.setPalette(palette) + + return root + + def set_status_message(self, message: str) -> None: + """Set status label text.""" + self._status.setText(message) + + def progress_show(self, show: bool = True) -> None: + """Set progress bar widget visible.""" + self._progress.setVisible(show) + + def progress_hide(self) -> None: + """Set progress bar widget unvisible.""" + self.progress_show(show=False) + + def set_progress_value(self, val: int) -> None: + """Set numeric value of progress bar.""" + self._progress.setValue(val) diff --git a/common/doc-dev/_build/.dummy b/qt/test/__init__.py similarity index 100% rename from common/doc-dev/_build/.dummy rename to qt/test/__init__.py diff --git a/qt/test/test_lint.py b/qt/test/test_lint.py new file mode 100644 index 000000000..81d03d57a --- /dev/null +++ b/qt/test/test_lint.py @@ -0,0 +1,446 @@ +# SPDX-FileCopyrightText: © 2023 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Tests using several linters. + +See file common/test/test_lint.py for details. +""" +import unittest +import os +import pathlib +import subprocess +import shutil +from typing import Iterable +from packaging import version + +BASE_REASON = ('Using package {0} is mandatory on TravisCI, on ' + 'other systems it runs only if `{0}` is available.') + +ON_TRAVIS = os.environ.get('TRAVIS', '') == 'true' +TRAVIS_REASON = ('Running linter tests is mandatory on TravisCI only. On ' + 'other machines they will run only if linters available.') + +PYLINT_AVAILABLE = shutil.which('pylint') is not None +RUFF_AVAILABLE = shutil.which('ruff') is not None +FLAKE8_AVAILABLE = shutil.which('flake8') is not None + +ANY_LINTER_AVAILABLE = any(( + PYLINT_AVAILABLE, + RUFF_AVAILABLE, + FLAKE8_AVAILABLE, +)) + +# "qt" directory +_base_dir = pathlib.Path(__file__).resolve().parent.parent + +""" + +""" +# To-Do: Invert the list +# Files in this lists will get the full battery of linters and rule sets. +full_test_files = [_base_dir / fp for fp in ( + 'aboutdlg.py', + # 'app.py', + 'bitwidgets.py', + 'confirmrestoredialog.py', + 'editusercallback.py', + 'encfsmsgbox.py', + 'event.py', + 'filedialog.py', + # 'icon.py', + 'languagedialog.py', + 'logviewdialog.py', + 'manageprofiles/__init__.py', + 'manageprofiles/combobox.py', + 'manageprofiles/copylinkswidget.py', + 'manageprofiles/excludesuggestions.py', + 'manageprofiles/schedulewidget.py', + 'manageprofiles/sectionedchecklist.py', + 'manageprofiles/sshkeyselector.py', + 'manageprofiles/spinboxunit.py', + 'manageprofiles/statebindcheckbox.py', + 'manageprofiles/storagesizewidget.py', + 'manageprofiles/sshproxywidget.py', + 'manageprofiles/tab_exclude.py', + 'manageprofiles/tab_expert_options.py', + 'manageprofiles/tab_general.py', + 'manageprofiles/tab_include.py', + 'manageprofiles/tab_options.py', + 'manageprofiles/tab_remove_retention.py', + 'messagebox.py', + 'placeswidget.py', + 'profile_operations.py', + 'plugins/notifyplugin.py', + 'plugins/systrayiconplugin.py', + # 'qtsystrayicon.py', + 'qttools.py', + 'qttools_path.py', + 'restoreconfigdialog.py', + 'restoredialog.py', + # 'serviceHelper.py', + 'shutdowndlg.py', + # 'snapshotsdialog.py', + 'statedata.py', + 'statusbar.py', + 'test/__init__.py', + 'test/test_lint.py', + 'test/test_profile_operations.py', + 'test/test_statedata.py', + 'test/test_timeline.py', + 'textdlg.py', + 'timeline.py', + 'usermessagedialog.py', +)] + +# Not all linters do respect PEP8 (e.g. ruff, PyLint) +PEP8_MAX_LINE_LENGTH = 79 + + +def create_pylint_cmd(include_error_codes=None): + """Create the pylint base command for later use with subprocess.run(). + + Args: + include_error_codes: List of exclusive rules to check for. If empty all + default rules are checked. + """ + + cmd = [ + 'pylint', + # Make sure BIT modules can be imported (to detect "no-member") + '--init-hook=import sys;' + 'sys.path.insert(0, "./../qt");' + 'sys.path.insert(0, "./../common");', + # Storing results in a pickle file is unnecessary + '--persistent=n', + # autodetec number of parallel jobs + '--jobs=0', + # Disable scoring ("Your code has been rated at xx/10") + '--score=n', + # prevent false-positive no-module-member errors + '--extension-pkg-allow-list=PyQt6,PyQt6.QtCore', + # Because of globally installed GNU gettext functions + '--additional-builtins=_,ngettext', + # PEP8 conform line length (see PyLint Issue #3078) + f'--max-line-length={PEP8_MAX_LINE_LENGTH}', + # Whitelist variable names + '--good-names=idx,fp,closeEvent', + ] + + if include_error_codes: + # Deactivate all checks by default + cmd.append('--disable=all') + # Include specific codes only + cmd.append('--enable=' + ','.join(include_error_codes)) + + return cmd + + +@unittest.skipUnless(ON_TRAVIS or ANY_LINTER_AVAILABLE, TRAVIS_REASON) +class MirrorMirrorOnTheWall(unittest.TestCase): + """Check all py-files in the package (incl. test files) for lints, + potential bugs and if they are compliant to the coding styles (e.g. PEP8). + """ + + @classmethod + def _collect_py_files(cls) -> Iterable[pathlib.Path]: + """All py-files related to that distribution package. + + Dev note (2023-11): Use package metadata after migration to + pyproject.toml. + """ + path = pathlib.Path.cwd() + + # Make sure we are inside the test folder + if path.name in ['qt', 'common']: # happens e.g. on TravisCI + path = path / 'test' + + if not path.name.startswith('test'): + raise RuntimeError('Something went wrong. The test should run ' + 'inside the test folder but the current folder ' + f'is {path}.') + + # Workaround + path = path.parent + + # Find recursive all py-files. + py_files = path.rglob('*.py') + + # Exclude full test files + return filter(lambda fp: fp not in full_test_files, py_files) + + @classmethod + def setUpClass(cls): + cls.collected_py_files = cls._collect_py_files() + + def test005_ensure_linter_versions(self): + """Workaround to ensure the correct linter versions are used. + + For sure there are better ways to solve this. But migration to a + standard python package format need to be done first. See #1575. + Until then this test will spare some hours of work, e.g. fixing linter + errors (from out-dated linters) that are not relevant anymore in + modern lintern versions. + + Another location where linter versions are relevant is CONTRIBUTING.md. + """ + + if PYLINT_AVAILABLE: + version_target = version.parse('3.3.0') + + proc = subprocess.run( + ['pylint', '--version'], + capture_output=True, + text=True, + check=True) + + version_string = proc.stdout.split('\n')[0].replace('pylint ', '') + version_actual = version.parse(version_string) + + self.assertTrue( + version_actual >= version_target, + f'PyLint version is {version_actual} but need to ' + f'be {version_target} or higher.') + + if RUFF_AVAILABLE: + version_target = version.parse('0.15.0') + + proc = subprocess.run( + ['ruff', '--version'], + capture_output=True, + text=True, + check=True) + + version_string = proc.stdout.split('\n')[0].replace('ruff ', '') + version_actual = version.parse(version_string) + + self.assertTrue( + version_actual >= version_target, + f'Ruff version is {version_actual} but need to ' + f'be {version_target} or higher.') + + @unittest.skipUnless(RUFF_AVAILABLE, BASE_REASON.format('ruff')) + def test010_ruff_default_ruleset(self): + """Ruff in default mode.""" + + # ATTENTIION: Some settings are found in pyproject.toml + cmd = [ + 'ruff', + 'check', + # Additionally activate subset of special rules: + # - PyLint (PL) + # - PyCodestyle (E, W) + # - flake8-gettext (INT) + # - useless noqua (RUF100) + # - pep8-naming (N) + # Consider UP, ANN, D, DOC (upgrade, annotation, pydocstyle, + # pydoclint) + '--extend-select=PL,E,W,INT,RUF100,N', + # Ignore: redefined-loop-name + '--ignore=PLW2901', + '--line-length', str(PEP8_MAX_LINE_LENGTH), + # Because of globally installed GNU gettext functions + '--config', 'builtins=["_", "ngettext"]', + # Ruff counting branches different from PyLint. + # See: + '--config', 'pylint.max-branches=13', + '--config', 'flake8-quotes.inline-quotes = "single"', + # one error per line (no context lines) + '--output-format=concise', + '--quiet', + ] + + cmd.extend(full_test_files) + + proc = subprocess.run( + cmd, + check=False, + universal_newlines=True, + capture_output=True + ) + + # No errors other then linter rules + self.assertIn(proc.returncode, [0, 1], proc.stderr) + + error_n = len(proc.stdout.splitlines()) + if error_n > 0: + print(proc.stdout) + + self.assertEqual(0, error_n, f'Ruff found {error_n} problem(s).') + + # any other errors? + self.assertEqual(proc.stderr, '') + + @unittest.skipUnless(FLAKE8_AVAILABLE, BASE_REASON.format('flake8')) + def test020_flake8_default_ruleset(self): + """Flake8 in default mode.""" + cmd = [ + 'flake8', + f'--max-line-length={PEP8_MAX_LINE_LENGTH}', + '--builtins=_,ngettext', + # '--enable-extensions=' + ] + + cmd.extend(full_test_files) + + proc = subprocess.run( + cmd, + check=False, + universal_newlines=True, + capture_output=True + ) + + error_n = len(proc.stdout.splitlines()) + if error_n > 0: + print(proc.stdout) + + self.assertEqual(0, error_n, f'Flake8 found {error_n} problem(s).') + + # any other errors? + self.assertEqual(proc.stderr, '') + + @unittest.skipUnless(PYLINT_AVAILABLE, BASE_REASON.format('PyLint')) + def test030_pylint_default_ruleset(self): + """Use Pylint with all default rules to check specific files. + """ + + cmd = create_pylint_cmd() + + # Add py-files + cmd.extend(full_test_files) + + r = subprocess.run( + cmd, + check=False, + universal_newlines=True, + capture_output=True) + + # Count lines except module headings + error_n = len(list(filter(lambda line: not line.startswith('*****'), + r.stdout.splitlines()))) + print(r.stdout) + + self.assertEqual(0, error_n, f'PyLint found {error_n} problems.') + + # any other errors? + self.assertEqual(r.stderr, '') + + @unittest.skipUnless(PYLINT_AVAILABLE, BASE_REASON.format('PyLint')) + def test050_pylint_reduced_ruleset(self): + """Use Pylint to check for specific rules only. + + Some facts about PyLint + - It is one of the slowest available linters. + - It is able to catch lints other linters miss. + """ + + # Explicit activate checks + err_codes = [ + # 'C0103', # invalid-name + 'C0114', # missing-module-docstring + 'C0115', # missing-class-docstring + # 'C0116', # missing-function-docstring + 'C0200', # consider-using-enumerate + 'C0201', # consider-iterating-dictionary + 'C0301', # line-too-long + 'C0303', # trailing-whitespace + 'C0305', # trailing-newlines + 'C0321', # multiple-statements + 'C0325', # superfluous-parens + 'C0410', # multiple-imports + # 'C0411', # wrong-import-order + # 'C0412', # ungouped-imports + # 'C0413', # wrong-import-position + 'E0100', # init-is-generator + 'E0101', # return-in-init + 'E0102', # function-redefined + 'E0103', # not-in-loop + 'E0106', # return-arg-in-generator + 'E0213', # no-self-argument + 'E0401', # import-error + 'E0602', # undefined-variable + 'E1101', # no-member + 'E1120', # no-value-for-parameter + 'E1121', # too-many-function-args + 'E1123', # unexpected-keyword-arg + 'E1129', # not-context-manager + 'I0021', # useless-suppression + 'R0202', # no-classmethod-decorator + 'R0203', # no-staticmethod-decorator + 'R0801', # duplicate-code + # 'R0902', # too-many-instance-attributes + 'R0904', # too-many-public-methods + 'R0912', # too-many-branches + 'R0913', # too-many-arguments + # 'R0915', # too-many-statements + 'W0101', # unreachable + # 'W0102', # dangerous-default-value + 'W0105', # pointless-string-statement + 'W0106', # expression-not-assigned + 'W0107', # unnecessary-pass + # 'W0120', # useless-else-on-loop + 'w0122', # exec-used + 'W0123', # eval-used + 'W0150', # lost-exception + 'W0201', # attribute-defined-outside-init + 'W0221', # arguments-differ + 'W0237', # arguments-renamed + 'W0311', # bad-indentation + 'W0404', # reimported + 'W0603', # global-statement + 'W0611', # unused-import + 'W0612', # unused-variable + 'W0613', # unused-argument + 'W0614', # unused-wildcard-import + # 'W0621', # redefined-outer-name + # 'W0622', # redefined-builtin + 'W0631', # undefinied-loop-variable + # 'W0640', # cell-var-from-loop + 'W0702', # bare-except + # 'W0703', # broad-except + 'W0707', # raise-missing-from + 'W0711', # binary-op-exception + 'W1115', # bad-format-string-key + 'W1301', # unused-format-string-key + 'W1401', # anomalous-backslash-in-string (invalid escape sequence) + 'W1511', # bad-thread-instantiation + 'W1515', # forgotten-debug-statement + # 'W4902', # deprecated-method + # 'W4903', # deprecated-argument + # 'W4904', # deprecated-class + 'R0202', # no-classmethod-decorator + 'R0203', # no-staticmethod-decorator + # 'R0911', # too-many-return-statements + # 'R0914', # too-many-locals + 'R1701', # simplifiable-if-statement + 'R1702', # too-many-nested-blocks + 'R1703', # simplifiable-if-expression + # 'R1705', # no-else-return + # 'R1720', # no-else-raise + ] + + cmd = create_pylint_cmd(err_codes) + + # Add py-files + cmd.extend(self._collect_py_files()) + + r = subprocess.run( + cmd, + check=False, + universal_newlines=True, + capture_output=True) + + # Count lines except module headings and output about duplicate code + error_n = len(list(filter( + lambda line: line[:2] not in ('**', ' ', '==', ' (', ''), + r.stdout.splitlines()))) + print(r.stdout) + + self.assertEqual(0, error_n, f'PyLint found {error_n} problems.') + + # any other errors? + self.assertEqual(r.stderr, '') diff --git a/qt/test/test_profile_operations.py b/qt/test/test_profile_operations.py new file mode 100644 index 000000000..430546450 --- /dev/null +++ b/qt/test/test_profile_operations.py @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: © 2026 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# pylint: disable=C0115, C0116 +"""Tests about ProfileOperations class""" +import unittest +from unittest.mock import Mock +from profile_operations import ProfileOperations + + +class AddInclude(unittest.TestCase): + def test_no_duplicates(self): + mock_config = Mock() + mock_config.include.return_value = [('A', 0), ('B', 1)] + + sut = ProfileOperations(1, mock_config) + + duplicates = sut.add_include(['C', 'D']) + + # return value + self.assertEqual(duplicates, []) + # first positional call argument + self.assertEqual( + [val for val, _ in mock_config.setInclude.call_args[0][0]], + ['A', 'B', 'C', 'D'] + ) + + def test_with_duplicates(self): + mock_config = Mock() + mock_config.include.return_value = [('A', 0), ('B', 1)] + + sut = ProfileOperations(1, mock_config) + + duplicates = sut.add_include(['C', 'B']) + + # return value + self.assertEqual(duplicates, ['B']) + # first positional call argument + self.assertEqual( + [val for val, _ in mock_config.setInclude.call_args[0][0]], + ['A', 'B', 'C'] + ) + + +class AddExclude(unittest.TestCase): + def test_no_duplicates(self): + mock_config = Mock() + mock_config.exclude.return_value = ['A', 'B'] + + sut = ProfileOperations(1, mock_config) + + duplicates = sut.add_exclude(['C', 'D']) + + # return value + self.assertEqual(duplicates, []) + # first positional call argument + self.assertEqual( + mock_config.setExclude.call_args[0][0], + ['A', 'B', 'C', 'D'] + ) + + def test_with_duplicates(self): + mock_config = Mock() + mock_config.exclude.return_value = ['A', 'B'] + + sut = ProfileOperations(1, mock_config) + + duplicates = sut.add_exclude(['C', 'B']) + + # return value + self.assertEqual(duplicates, ['B']) + # first positional call argument + self.assertEqual( + mock_config.setExclude.call_args[0][0], + ['A', 'B', 'C'] + ) diff --git a/qt/test/test_statedata.py b/qt/test/test_statedata.py new file mode 100644 index 000000000..7214cff4f --- /dev/null +++ b/qt/test/test_statedata.py @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: © 2025 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Tests about statefile module.""" +# pylint: disable=wrong-import-position,wrong-import-order +import unittest +from qttools_path import register_backintime_path +register_backintime_path('common') +import statedata # noqa: E402 + + +class IsSingleton(unittest.TestCase): + """StateData instance is a singleton.""" + + @classmethod + def tearDownClass(cls): + # Delete existing StateData instance + try: + # pylint: disable-next=protected-access + del statedata.StateData._instances[statedata.StateData] + except KeyError: + pass + + def setUp(self): + # Clean up all instances + try: + # pylint: disable-next=protected-access + del statedata.StateData._instances[statedata.StateData] + except KeyError: + pass + + def test_identity(self): + """Identical identity.""" + one = statedata.StateData() + two = statedata.StateData() + + self.assertEqual(id(one), id(two)) + + def test_content(self): + """Identical values.""" + one = statedata.StateData() + two = statedata.StateData() + + one['foobar'] = 7 + + self.assertEqual(one, two) + + +class Properties(unittest.TestCase): + """Property access without errors.""" + + @classmethod + def tearDownClass(cls): + # Delete existing StateData instance + try: + # pylint: disable-next=protected-access + del statedata.StateData._instances[statedata.StateData] + except KeyError: + pass + + def setUp(self): + # Delete existing StateData instance + try: + # pylint: disable-next=protected-access + del statedata.StateData._instances[statedata.StateData] + except KeyError: + pass + + def test_read_empty_global(self): + """Read properties from empty state data""" + sut = statedata.StateData() + + self.assertEqual(sut.msg_release_candidate, None) + self.assertEqual(sut.msg_encfs_global, False) + self.assertEqual(sut.mainwindow_show_hidden, False) + self.assertEqual(sut.files_view_sorting, (0, 0)) + self.assertEqual(sut.mainwindow_main_splitter_widths, (150, 450)) + self.assertEqual(sut.mainwindow_second_splitter_widths, (150, 300)) + + with self.assertRaises(KeyError): + # pylint: disable=pointless-statement + sut.mainwindow_coords + sut.mainwindow_dims + sut.logview_dims + sut.files_view_col_widths + + def test_profile_not_exist(self): + """Profile does not exists.""" + sut = statedata.StateData() + profile = sut.profile(42) + + with self.assertRaises(KeyError): + # pylint: disable=pointless-statement + profile.last_path diff --git a/qt/test/test_timeline.py b/qt/test/test_timeline.py new file mode 100644 index 000000000..547cd428b --- /dev/null +++ b/qt/test/test_timeline.py @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: © 2026 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Tests about timeline widget.""" +# pylint: disable=wrong-import-position,wrong-import-order +import unittest +from datetime import date +from qttools_path import register_backintime_path +register_backintime_path('common') +import timeline # noqa: E402 + +STRFTIME = '%Y-%m-%d %a %H:%M' + + +def _datetime_to_str(result): + for idx, val in enumerate(result): + result[idx] = ( + val[0], + val[1].strftime(STRFTIME), + val[2].strftime(STRFTIME) + ) + + return result + + +class Periods(unittest.TestCase): + """StateData instance is a singleton.""" + # pylint: disable=protected-access,missing-function-docstring + + def test_simple_a(self): + """Simple situations without edge cases""" + today = date(2026, 3, 28) # Saturday + sut = timeline._calculate_timeline_periods(today) + + expect = [ + ('Today', '2026-03-28 Sat 00:00', '2026-03-28 Sat 23:59'), + ('Yesterday', '2026-03-27 Fri 00:00', '2026-03-27 Fri 23:59'), + ('This week', '2026-03-23 Mon 00:00', '2026-03-26 Thu 23:59'), + ('Last week', '2026-03-16 Mon 00:00', '2026-03-22 Sun 23:59'), + ('This month', '2026-03-01 Sun 00:00', '2026-03-15 Sun 23:59'), + ('Last month', '2026-02-01 Sun 00:00', '2026-02-28 Sat 23:59'), + ] + + self.assertEqual( + _datetime_to_str(sut), + expect + ) + + def test_simple_b(self): + today = date(2026, 3, 18) + sut = timeline._calculate_timeline_periods(today) + + expect = [ + ('Today', '2026-03-18 Wed 00:00', '2026-03-18 Wed 23:59'), + ('Yesterday', '2026-03-17 Tue 00:00', '2026-03-17 Tue 23:59'), + ('This week', '2026-03-16 Mon 00:00', '2026-03-16 Mon 23:59'), + ('Last week', '2026-03-09 Mon 00:00', '2026-03-15 Sun 23:59'), + ('This month', '2026-03-01 Sun 00:00', '2026-03-08 Sun 23:59'), + ('Last month', '2026-02-01 Sun 00:00', '2026-02-28 Sat 23:59'), + ] + + self.assertEqual( + _datetime_to_str(sut), + expect + ) + + def test_simple_c(self): + today = date(2026, 3, 12) + sut = timeline._calculate_timeline_periods(today) + + expect = [ + ('Today', '2026-03-12 Thu 00:00', '2026-03-12 Thu 23:59'), + ('Yesterday', '2026-03-11 Wed 00:00', '2026-03-11 Wed 23:59'), + ('This week', '2026-03-09 Mon 00:00', '2026-03-10 Tue 23:59'), + ('Last week', '2026-03-02 Mon 00:00', '2026-03-08 Sun 23:59'), + ('This month', '2026-03-01 Sun 00:00', '2026-03-01 Sun 23:59'), + ('Last month', '2026-02-01 Sun 00:00', '2026-02-28 Sat 23:59'), + ] + + self.assertEqual(_datetime_to_str(sut), expect) + + def test_last_week_overlap_last_month(self): + """Without 'This month' and shorter 'Last month' + + This months, is covered by all previous periods in the list. + Last months is shorted because of Last week lapping into the last + months. + """ + today = date(2026, 3, 7) + sut = timeline._calculate_timeline_periods(today) + + expect = [ + ('Today', '2026-03-07 Sat 00:00', '2026-03-07 Sat 23:59'), + ('Yesterday', '2026-03-06 Fri 00:00', '2026-03-06 Fri 23:59'), + ('This week', '2026-03-02 Mon 00:00', '2026-03-05 Thu 23:59'), + ('Last week', '2026-02-23 Mon 00:00', '2026-03-01 Sun 23:59'), + ('Last month', '2026-02-01 Sun 00:00', '2026-02-22 Sun 23:59'), + ] + + self.assertEqual(_datetime_to_str(sut), expect) + + def test_this_week_overlap_yesterday(self): + """Without 'This week' because it touches 'Yesterday'. + """ + today = date(2026, 3, 3) + sut = timeline._calculate_timeline_periods(today) + + expect = [ + ('Today', '2026-03-03 Tue 00:00', '2026-03-03 Tue 23:59'), + ('Yesterday', '2026-03-02 Mon 00:00', '2026-03-02 Mon 23:59'), + ('Last week', '2026-02-23 Mon 00:00', '2026-03-01 Sun 23:59'), + ('Last month', '2026-02-01 Sun 00:00', '2026-02-22 Sun 23:59'), + ] + + self.assertEqual(_datetime_to_str(sut), expect) diff --git a/qt/textdlg.py b/qt/textdlg.py new file mode 100644 index 000000000..972faa467 --- /dev/null +++ b/qt/textdlg.py @@ -0,0 +1,159 @@ +# SPDX-FileCopyrightText: © 2025 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""A dialog containing a QTextBrowser""" +from PyQt6.QtWidgets import QDialog, QTextBrowser, QVBoxLayout +from PyQt6.QtGui import (QDesktopServices, + QFontDatabase, + QGuiApplication, + QIcon, + QTextCursor) +from PyQt6.QtCore import QRegularExpression, QTimer, QUrl + + +class TextDialog(QDialog): + """A dialog containing a QTextBrowser capable of markdown and plain text + + Dev note: Consider joining the center-resize-feature with + RestoreConfigDialog. + """ + # pylint: disable=too-few-public-methods + DEFAULT_WIDTH_FRACTION = 0.5 + DEFAULT_HEIGHT_FRACTION = 0.75 + + # pylint: disable-next=too-many-arguments,too-many-positional-arguments + def __init__(self, # noqa: PLR0913 + content: str, + markdown: bool = False, + html: bool = False, + scroll_to: str = None, + title: str = '', + icon: QIcon = None, + width_fraction: float = DEFAULT_WIDTH_FRACTION, + height_fraction: float = DEFAULT_HEIGHT_FRACTION): + """ + Args: + width_fraction: Fraction of screen width (0.0 to 1.0) + height_fraction: Fraction of screen height (0.0 to 1.0) + """ + super().__init__() + self.setWindowTitle(title) + + self._scroll_to_pattern = scroll_to + self._markdown = markdown + self._html = html + self._resize_tries = -1 + self._height_fraction = height_fraction + self._width_fraction = width_fraction + + if icon: + self.setWindowIcon(icon) + + self._browser = QTextBrowser() + font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont) + self._browser.setFont(font) + + self._browser.setOpenLinks(False) + self._browser.setOpenExternalLinks(False) + self._browser.anchorClicked.connect(self._on_link_clicked) + + layout = QVBoxLayout(self) + layout.addWidget(self._browser) + + self.set_content(content) + + @property + def width_fraction(self) -> float: + """Fraction of screen width""" + return self._width_fraction + + @property + def browser_widget(self) -> QTextBrowser: + """The primary widget""" + return self._browser + + def _on_link_clicked(self, url: QUrl): + if url.scheme() in ('http', 'https'): + QDesktopServices.openUrl(url) + else: + self._browser.setSource(url) + + def set_content(self, content: str): + """Set content to the primary widget. + """ + if self._markdown: + self._browser.setMarkdown(content) + elif self._html: + self._browser.setHtml(content) + else: + self._browser.setPlainText(content) + + # See _resize_to_full_height() for details. + self._resize_tries = 10 + QTimer.singleShot(1, self._center_and_resize) + + def _center_and_resize(self) -> None: + """Center the dialog and resize it to fractions of the screen size. + """ + # pylint: disable=duplicate-code + + # Determine the height of the dialog's title bar and border. This + # value is unknown or incorrect until the dialg is fully drawn. + # That is the reason why we use this workaround. + deco_height = self.frameGeometry().height() - self.geometry().height() + if deco_height == 0 and self._resize_tries > 0: + self._resize_tries -= 1 + QTimer.singleShot(1, self._center_and_resize) + return + + handle = self.windowHandle() + screen = handle.screen() if handle else QGuiApplication.primaryScreen() + geom = screen.availableGeometry() + + new_width = int(geom.width() * self._width_fraction) + new_height = int((geom.height() - deco_height) * self._height_fraction) + + self.move( + # center horizontal + geom.center().x() - (new_width // 2), + # center vertical + geom.center().y() - (new_height // 2) + ) + self.resize( + # desired width + new_width, + # desired height (incl. window decoration) on available screen + new_height) + + self._scroll_to() + + def _scroll_to(self): + if self._scroll_to_pattern is None: + return + + cursor = self._browser.document().find( + QRegularExpression(self._scroll_to_pattern), + QTextCursor() + ) + + if cursor.isNull(): + return + + # Sets the cursor to the text position. + self._browser.setTextCursor(cursor) + # Scroll until the selected text is visible. + self._browser.ensureCursorVisible() + + # The selected text appears at the end of the widget not at the top. + # This adjusts the position accordingly via scrolling one page up. + scrollbar = self._browser.verticalScrollBar() + cursor_rect = self._browser.cursorRect(cursor) + margin = 10 + new_value = scrollbar.value() + cursor_rect.top() - margin + new_value = max( + scrollbar.minimum(), min(scrollbar.maximum(), new_value)) + scrollbar.setValue(new_value) diff --git a/qt/timeline.py b/qt/timeline.py new file mode 100644 index 000000000..f8b4fc562 --- /dev/null +++ b/qt/timeline.py @@ -0,0 +1,429 @@ +# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan +# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning +# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey +# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze +# SPDX-FileCopyrightText: © 2024 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +# +# File was split from "qt/qttools.py". +"""Time line widget. +""" +from datetime import (datetime, date, timedelta) +from calendar import monthrange +from contextlib import contextmanager +from functools import partial +from PyQt6.QtGui import QFont, QPalette +from PyQt6.QtCore import Qt, QTimer +from PyQt6.QtWidgets import (QAbstractItemView, + QApplication, + QTreeWidget, + QTreeWidgetItem) +import logger # workaround. shouldn't be necessary +import qttools +from event import Event +from qttools_path import register_backintime_path +register_backintime_path('common') + +try: + _('Warning') +except NameError: + def _(txt): + return txt + + +def start_of_day(day: date) -> datetime: + """Add 00:00 to a date object""" + return datetime.combine(day, datetime.min.time()) + + +def end_of_day(day: date) -> datetime: + """Add 23:59 to a date object""" + return datetime.combine(day, datetime.max.time()) + + +def _calculate_timeline_periods(today: date = date.today() + ) -> list[tuple[str, datetime, datetime]]: + """Calculate timestamps for the sub-headers. + + The headers are Today, Yesterday, This Week, Last Week, This Month and + Last Month. + + Returns: + A list of tuples with label, start and end datetime of each period. + """ + result = [] + + # Today + today_min = start_of_day(today) + today_max = end_of_day(today) + result.append((_('Today'), today_min, today_max)) + + # Yesterday + yesterday_min = start_of_day(today - timedelta(days=1)) + yesterday_max = end_of_day(today_min - timedelta(hours=1)) + result.append((_('Yesterday'), yesterday_min, yesterday_max)) + + # This week, but not yesterday or today + this_week_min = start_of_day(today - timedelta(today.weekday())) + this_week_max = end_of_day(yesterday_min - timedelta(days=1)) + # Add only, if not overlapping with Yesterday + if this_week_max > this_week_min: + result.append((_('This week'), this_week_min, this_week_max)) + + # Last week + last_week_min = start_of_day(today - timedelta(today.weekday() + 7)) + last_week_max = end_of_day(last_week_min + timedelta(days=6)) + result.append((_('Last week'), last_week_min, last_week_max)) + + # This month + if (today.month == last_week_min.month + and today.month == this_week_min.month): + this_month_min = start_of_day(today.replace(day=1)) + this_month_max = end_of_day(last_week_min - timedelta(days=1)) + result.append((_('This month'), this_month_min, this_month_max)) + + # Last months + last_month_max = end_of_day(today.replace(day=1) - timedelta(days=1)) + last_month_min = start_of_day(last_month_max.replace(day=1)) + if last_month_max.date() >= last_week_min.date(): + last_month_max = end_of_day(last_week_min.date() - timedelta(days=1)) + result.append((_('Last month'), last_month_min, last_month_max)) + + return result + + +class TimeLine(QTreeWidget): + """A list like widget containing existing backups. + + The widget is placed on the right side of the main window. + """ + + # event_selection_changed = Event() + event_now_selected = Event() + event_backup_selected = Event() + + def __init__(self, parent): + super().__init__(parent) + self.setRootIsDecorated(False) + self.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + self.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection) + + self.setHeaderLabels([_('Backups')]) + + self.setSortingEnabled(True) + self.sortByColumn(0, Qt.SortOrder.DescendingOrder) + + self.header().setSectionsClickable(False) + + self.parent = parent + self.snapshots = parent.snapshots + + # Used headers. A of tuples with (label, start, end) + self._header_data = None + + # Timestamp boundaries (from-to) used for the header items: + # Today, Yesterday, This Week, Last Week, This Months, Last Months + self._default_header_data = None + + # Helper variable. Timestamps older than this need and extra header, + # other than a default header. + self._specific_month_boundary = None + + self.clear_and_reset() + + self.itemSelectionChanged.connect(self._on_item_selection_changed) + + def _on_item_selection_changed(self): + """Selection event handler distinguishing between 'Now' and backup + entries. + """ + if self.is_now_selected(): + self.event_now_selected.notify() + return + + self.event_backup_selected.notify( + self.selected_backup_descriptor) + + def clear_and_reset(self): + """Remove all entries, recalculate header data and add 'Now' entry""" + + with qttools.block_paint_updates(self): + super().clear() + self._header_data = [] + self._default_header_data = _calculate_timeline_periods() + self._specific_month_boundary = self._default_header_data[-1][1] + + # "Now" + self.addTopLevelItem(NowEntry()) + + def create_backup_entry(self, + descriptor: str, + timestamp: datetime, + last_checked: str, + label: str): + """Create and add an item representing backup. + + Also add a header if not already present. + """ + item = BackupEntry( + descriptor=descriptor, + timestamp=timestamp, + last_checked=last_checked, + label=label + ) + + self.addTopLevelItem(item) + self._create_header_if_necessary(timestamp) + + def _header_in_use(self, backup_timestamp: datetime) -> bool: + """Check if the backup timestamp fit into an already existing + header item.""" + + for _text, start, end in self._header_data: + if start <= backup_timestamp <= end: + return True + + return False + + def _create_header_if_necessary(self, backup_timestamp: datetime): + """Create an header entry for the backup timestamp if necessary""" + + # Already used header fit this timestamp? + if self._header_in_use(backup_timestamp): + return + + # Use a default header? + if backup_timestamp >= self._specific_month_boundary: + for label, start, end in self._default_header_data: + if start <= backup_timestamp <= end: + self._create_header_entry(label, start, end) + return + + logger.critical( + 'Unexpected situation. Found no default header for ' + f'{backup_timestamp=} in {self._default_header_data=}' + ) + + # Create an extra months header + + # Calculate start and end of backup months + start = backup_timestamp.date().replace(day=1) + end = start.replace(day=monthrange(start.year, start.month)[1]) + + label = start.strftime( + '%B' if start.year == date.today().year else '%B, %Y' + ) + label = label.capitalize() + + self._create_header_entry(label, start_of_day(start), end_of_day(end)) + + def _create_header_entry(self, label, start, end): + item = HeaderEntry(label, end) + self.addTopLevelItem(item) + self._header_data.append((label, start, end)) + + # # DEBUG + # item.setToolTip( + # 0, + # f'DEBUG - {start.strftime("%c")} to {end.strftime("%c")}' + # ) + + return True + + @contextmanager + def preserve_selection(self): + """Context manager preserving the selection of the current selected + item. If no selection exists the 'Now' item will be selected. + """ + previous_selection = self.selected_backup_descriptor() + + try: + yield + + finally: + if previous_selection: + # Select the backup + callback_func = partial( + self.select_by_descriptor, previous_selection + ) + else: + # "Now" if previously no backup was selected + callback_func = self.select_now + + QTimer.singleShot(0, callback_func) + + def select_now(self): + """Select the first item, which is the 'Now' entry""" + self._set_current_item(self.topLevelItem(0)) + + def get_all_selected_backup_descriptors(self) -> list[str]: + """A list of all selected backup descriptors""" + return [item.descriptor for item in self.selectedItems()] + + def get_all_selected_backup_labels(self) -> list[str]: + """A list labels of all selected backups""" + return [item.label for item in self.selectedItems()] + + def is_now_selected(self) -> bool: + """If the 'Now' item, the first in the widget, is selected.""" + model_index = self.currentIndex() + return model_index.row() == 0 + + def selected_backup_descriptor(self) -> str: + """Return the backup descriptor of the current selected item. + + Return: + The descriptor (formally known as 'sid') as a string. + """ + if self.is_now_selected(): + return None + + item = self.currentItem() + + return item.descriptor if item else None + + def selected_backup_label(self) -> str: + """Return the label used of the current selected item. + + That is the backup descriptor plus a name string if defined + by the user. + """ + if self.is_now_selected(): + return None + + item = self.currentItem() + + return item.label if item else None + + def select_by_descriptor(self, backup_descriptor: str): + """Select backup entry related to the descriptor.""" + for item in self.iter_backup_items(): + if item.descriptor == backup_descriptor: + self._set_current_item(item) + break + + def select_all_backup_entries(self): + """Highlight all backup entries.""" + self.clearSelection() + for item in self.iter_backup_items(): + item.setSelected(True) + + def _set_current_item(self, item, *args, **kwargs): + self.setCurrentItem(item, *args, **kwargs) + # self.event_selection_changed.notify() + + def _iter_items(self): + for index in range(self.topLevelItemCount()): + yield self.topLevelItem(index) + + def iter_backup_items(self): + """Iterate over all items.""" + for item in self._iter_items(): + if isinstance(item, BackupEntry): + yield item + + def _iter_header_items(self): + for item in self._iter_items(): + if isinstance(item, HeaderEntry): + yield item + + +# pylint: disable-next=too-few-public-methods +class _TimeLineItemBase(QTreeWidgetItem): + """Backup entry widget used in TimeLine.""" + + def __init__(self, + timestamp: datetime, + tooltip: str, + label: str): + super().__init__() + + if tooltip: + # DEBUG + # self.setToolTip(0, f'{type(self)} {tooltip}') + self.setToolTip(0, tooltip) + + self.setText(0, label) + self.setData(0, Qt.ItemDataRole.UserRole, timestamp) + + def __lt__(self, other): + return self.data(0, Qt.ItemDataRole.UserRole) \ + < other.data(0, Qt.ItemDataRole.UserRole) + + +class BackupEntry(_TimeLineItemBase): + """Backup entry widget used in TimeLine.""" + + def __init__(self, + descriptor: str, + timestamp: datetime, + last_checked: str, + label: str): + + tooltip = _('Last check {time}').format(time=last_checked) + + super().__init__( + timestamp=timestamp, + tooltip=tooltip, + label=label + ) + + self._descriptor = descriptor + + @property + def descriptor(self) -> str: + """Descriptor (sid) of the related backup.""" + return self._descriptor + + @property + def label(self) -> str: + """Text label of the entry.""" + return self.text(0) + + +# pylint: disable-next=too-few-public-methods +class NowEntry(_TimeLineItemBase): + """Now entry widget used in TimeLine.""" + + def __init__(self): + super().__init__( + timestamp=datetime.max, + tooltip=_( + 'These are your files as they are right now. ' + 'It is not a backup.' + ), + label=_('Now') + ) + + +class HeaderEntry(_TimeLineItemBase): # pylint: disable=too-few-public-methods + """Header entry widget used in TimeLine.""" + + def __init__(self, label: str, timestamp: datetime): + """ + Dev note (buhtz, 2024-01-14): Parts of that code are redundant with + app.py::MainWindow.addPlace(). + """ + super().__init__( + timestamp=timestamp, + tooltip=None, + label=label + ) + + font = self.font(0) + font.setWeight(QFont.Weight.Bold) + self.setFont(0, font) + + palette = QApplication.instance().palette() + self.setForeground( + 0, palette.color(QPalette.ColorRole.PlaceholderText)) + self.setBackground( + 0, palette.color(QPalette.ColorRole.AlternateBase)) + + self.setFlags(Qt.ItemFlag.NoItemFlags) diff --git a/qt/usermessagedialog.py b/qt/usermessagedialog.py new file mode 100644 index 000000000..779a08524 --- /dev/null +++ b/qt/usermessagedialog.py @@ -0,0 +1,127 @@ +# SPDX-FileCopyrightText: © 2023 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""Module about UserMessageDialog""" +from typing import Optional +from PyQt6.QtCore import Qt, QSize, QTimer +from PyQt6.QtGui import QCursor, QGuiApplication +from PyQt6.QtWidgets import (QDialog, + QLayout, + QVBoxLayout, + QDialogButtonBox, + QLabel, + QToolTip, + QWidget) + + +class UserMessageDialog(QDialog): + """Present a large messages to the users with extra features. + + Different from QMessageBox this dialog is intended to display large amount + of text. The dialog is able to wrap the text while being resized. + Hyperlinks in this text do display their URL as tooltip. + + HTML tags supported because of Qt rich text feature. Text between Newline + characters ``\n`` will be converted into ``

`` paragraphs. + + The dialog is centered relative to its parent if present, otherwise to the + screen. + """ + + def __init__( + self, + parent: Optional[QWidget], + title: str, + full_label: str, + ): + super().__init__(parent) + # screen_width = QApplication.primaryScreen().size().width() + # min_width = 300 if screen_width <= 1080 else 450 + self.setMinimumWidth(400) + + self.setWindowTitle(title) + self.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, True) + + # Wrap paragraphs in

tags. + if '\n' in full_label: + result = '' + for t in full_label.split('\n'): + result = f'{result}

{t}

' + + else: + result = full_label + + widget = QLabel(result, self) + widget.setWordWrap(True) + widget.setOpenExternalLinks(True) + widget.linkHovered.connect(self.slot_link_hovered) + + button = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok, self) + button.clicked.connect(self.accept) + + layout = QVBoxLayout(self) + layout.addWidget(widget) + layout.addWidget(button) + + self._fix_size() + + def _fix_size(self): + """The dialog is resized so it fits the content of the QLabel. + + Credits: https://stackoverflow.com/a/77012305/4865723 + """ + best = QLayout.closestAcceptableSize(self, QSize(self.width(), 1)) + + if self.height() < best.height(): + self.resize(best) + + QTimer.singleShot(0, self._center) + + def _center(self): + if self.parentWidget(): + self._center_to_parent() + + else: + self._center_to_screen() + + def _center_to_parent(self): + geo = self.frameGeometry() + geo.moveCenter(self.parentWidget().frameGeometry().center()) + self.move(geo.topLeft()) + + def _center_to_screen(self): + """Center the dialog to screen""" + + handle = self.windowHandle() + screen = handle.screen() if handle else QGuiApplication.primaryScreen() + + if not screen: + return + + geom = screen.availableGeometry() + + self.move( + # center horizontal + geom.center().x() - (self.geometry().width() // 2), + # center vertical + geom.center().y() - (self.geometry().height() // 2), + ) + + # pylint: disable-next=invalid-name + def resizeEvent(self, event): # noqa: N802 + """See `_fixSize()` for details.""" + super().resizeEvent(event) + + if event.oldSize().width() != event.size().width(): + QTimer.singleShot(0, self._fix_size) + + elif event.spontaneous(): + self._fix_size() + + def slot_link_hovered(self, url): + """Show URL in tooltip without anoing http-protocol prefixf.""" + QToolTip.showText(QCursor.pos(), url.replace('https://', '')) diff --git a/update_language_files.py b/update_language_files.py new file mode 100755 index 000000000..b7345b86e --- /dev/null +++ b/update_language_files.py @@ -0,0 +1,1053 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: © 2023 Christian BUHTZ +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In Time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . +"""This helper script does manage transferring translations to and from the +translation platform (currently Weblate). +""" +import sys +import datetime +import re +import tempfile +import string +import shutil +import json +from pathlib import Path +from subprocess import run, check_output +from lxml import etree +from common import languages, version + +try: + import polib + print(f'polib version: {polib.__version__}') +except ImportError: + # pylint: disable-next=raise-missing-from + raise ImportError('Can not import package "polib". Please install it.') + +# In usual GNU gettext environments it would be "locale" (sometimes plurarl +# "locales") +LOCAL_DIR = Path('common') / 'po' +TEMPLATE_PO = LOCAL_DIR / 'messages.pot' +LANGUAGE_NAMES_PY = Path('common') / 'languages.py' +GUI_DIR = Path('qt') +DESKTOP_FILE_FIELDS = ['GenericName', 'Comment'] +WEBLATE_URL = 'https://translate.codeberg.org/git/backintime/common' +PACKAGE_NAME = 'Back In Time' +PACKAGE_VERSION = version.__version__ +BUG_ADDRESS = 'https://github.com/bit-team/backintime' +# RegEx pattern: Character & followed by a word character (extract as group) +REX_SHORTCUT_LETTER = re.compile(r'&(\w)') +TRANSLATION_PLACEHOLDER_MSGID = 'translator-credits-placeholder' +DEFAULT_COPYRIGHT = '© 2008 Back In Time Team ' +MISSING_TRANSLATORS_TXT = "Strict and accurate recording of translators' " \ + "names began around 2022. As\n" \ + "the project started in 2008, some earlier translators' names " \ + 'may not have\n' \ + 'been documented and could be lost.' + + +def dict_as_code(a_dict: dict, indent_level: int) -> list[str]: + """Convert a (nested) Python dict into its PEP8 conform as-in-code + representation. + """ + tab = ' ' * 4 * indent_level + result = [] + for key in a_dict: + + # single quotes? + quote_key = "'" if isinstance(key, str) else "" + quote_val = "'" if isinstance(a_dict[key], str) else "" + + # A nested dict + if isinstance(a_dict[key], dict): + result.append(f"{tab}{quote_key}{key}{quote_key}: {{") + + result.extend( + dict_as_code(a_dict[key], indent_level+1)) + + result.append(f"{tab}}},") + continue + + # Regular key: value pair + result.append(f"{tab}{quote_key}{key}{quote_key}: " + f"{quote_val}{a_dict[key]}{quote_val},") + + return result + + +def update_po_template(): + """The po template file is update via `xgettext`. + + All files with extension `*.py` are scanned for translatable strings. + Unittest files and folders are excluded. + + xgettext is used instead of pygettext because the latter is deprecated + since xgettext is able to handle Python files. + """ + + print(f'Updating PO template file "{TEMPLATE_PO}" …') + + # Recursive search of Python files excluding unittest files and folders + find_cmd = [ + 'find', + # folders to search in + 'common', 'qt', + # look for py-files + '-name', '*.py', + # exclude files/folders related to unittests + '-not', '-name', 'test_*', + '-not', '-path', '*/test/*', + '-not', '-path', '*/tests/*' + ] + print(f'Execute "{find_cmd}".') + + py_files = check_output(find_cmd, text=True).split() + + print('Scan following files for translatable strings:\n{}' + .format('\n'.join(py_files))) + + cmd = [ + 'xgettext', + '--verbose', + '--language=Python', + f'--package-name="{PACKAGE_NAME}"', + f'--package-version="{PACKAGE_VERSION}"', + f'--msgid-bugs-address={BUG_ADDRESS}', + f'--output={TEMPLATE_PO}', + '--sort-by-file', + # '--sort-output', + ] + cmd.extend(py_files) + + print(f'Execute "{cmd}".') + run(cmd, check=True) + + _update_po_template_from_polkit_policies() + + _update_po_template_from_desktop_files() + + _add_spdx_header_to_po_template() + + +def _add_spdx_header_to_po_template(): + print(f'Add SPDX Header to PO template file "{TEMPLATE_PO}" …') + + # Header comment with SPDX data + spdx_base = get_spdx_metadata_lines(ignore_copyright=True, + without_comment_prefix=True) + + copyright = f'SPDX-FileCopyrightText: {DEFAULT_COPYRIGHT}' + + pof = polib.pofile(TEMPLATE_PO) + + print(f'\n{len(pof)} entries in {TEMPLATE_PO}') + + pof.header = f'{DEFAULT_COPYRIGHT}\n{spdx_base}\n{MISSING_TRANSLATORS_TXT}' + pof.save() + + +def _update_po_template_from_polkit_policies(): + """Extract translatable strings from polkit related policy files. + + Policy files are XML content. Translatable strings are marked by + an "gettext-domain" attribute. That strings are used in the + password request dialogs used by polkit agents; e.g. when starting + BIT in root mode or modifying Udev rules. + """ + + path = Path.cwd() / 'qt' + + for policy_fp in path.glob('*.policy'): + print( + f'Update PO template file with policy strings from {policy_fp} …') + + parser = etree.XMLParser(remove_comments=False) + tree = etree.parse(policy_fp, parser) + root = tree.getroot() + + # All tags containing attribute "gettext-domin='backintime'" + for elem in root.xpath(".//*[@gettext-domain='backintime']"): + # ignore empty fields + if not elem.text: + continue + + # Add to messages.pot, consider duplicates and location/occurrence + _add_entry_to_po_template( + msgid=elem.text.strip(), + fp=policy_fp.relative_to(Path.cwd()), + linenr=elem.sourceline + ) + + +def _update_po_template_from_desktop_files(): + # Each qt/*.desktop file + for desktop_fp in all_desktop_files_in_qt_dir(): + print(f'Update PO template file with strings from {desktop_fp} …') + content = desktop_fp.read_text(encoding='utf-8') + + for idx, line in enumerate(content.split('\n'), start=1): + + # ignore comments + if line.startswith('#'): + continue + + try: + field, value = line.split('=', 1) + except ValueError: + continue + + if field not in DESKTOP_FILE_FIELDS: + continue + + _add_entry_to_po_template( + msgid=value, + fp=desktop_fp, + linenr=idx + ) + + +def _add_entry_to_po_template(msgid: str, + fp: Path, + linenr: int): + + po_file = polib.pofile(TEMPLATE_PO) + + entry = po_file.find(msgid) + location = (fp, linenr) + + if entry: + # Update location + if location not in entry.occurrences: + entry.occurrences.append(location) + + else: + # Add new entry + po_file.append( + polib.POEntry( + msgid=msgid, + msgstr='', + occurrences=[location], + ) + ) + + po_file.save(TEMPLATE_PO) + + +def update_po_language_files(remove_obsolete_entries: bool = False): + """The po files are updated with the source strings from the pot-file (the + template for each po-file). + + The GNU gettext utility ``msgmerge`` is used for that. + + Make sure that the function `update_po_template()` is called beforehand. + """ + + print( + 'Update language (po) files' + + ' and remove obsolete entries' if remove_obsolete_entries else '' + ) + + spdx_base = get_spdx_metadata_lines(ignore_copyright=True, + without_comment_prefix=True) + + # Recursive all po-files + for po_path in LOCAL_DIR.rglob('**/*.po'): + + lang = po_path.stem + + cmd = [ + 'msgmerge', + '--verbose', + f'--lang={lang}', + '--update', + '--sort-by-file', + '--backup=off', # don't create *.po~ files + f'{po_path}', + f'{TEMPLATE_PO}' + ] + run(cmd, check=True) + + if remove_obsolete_entries: + # remove obsolete entries ("#~ msgid) + cmd = [ + 'msgattrib', + '--no-obsolete', + f'--output-file={po_path}', + f'{po_path}' + ] + run(cmd, check=True) + + _set_header(po_path, spdx_base) + + +def update_desktop_files(): + for desktop_fp in all_desktop_files_in_qt_dir(): + print(f'Update desktop file {desktop_fp} with translations …') + content = desktop_fp.read_text(encoding='utf-8') + content = content.split('\n') + + to_translate = {key: None for key in DESKTOP_FILE_FIELDS} + + # iterate from the end to the start + for idx, line in reversed(list(enumerate(content[:]))): + + # ignore comments + if line.startswith('#'): + continue + + # blank line? + if not line: + content = content[:idx] + content[idx+1:] + continue + + try: + field, value = line.split('=', 1) + except ValueError: + continue + + # each translatable or translated field + for target_field in DESKTOP_FILE_FIELDS: + if not field.startswith(target_field): + continue + + # translated field + if field.startswith(f'{target_field}['): + # remove translation + content = content[:idx] + content[idx+1:] + continue + + # remember original string + to_translate[target_field] = value + continue + + # PLAUSI + if field != target_field: + raise RuntimeError( + f'Unexpected situation. {target_field=} {field=} ' + f'{value=} {line=}' + ) + + for field, value in to_translate.items(): + # print(f'{field=} {value=}') + translations = _get_translation_for_desktop_string(value) + + # Dev note (2026-03): Still not sure but it seems the length + # restriction was a misunderstanding on my site. + # if field == 'Comment': + # _check_value_length(translations, field) + + translations = [ + f'{field}[{lang}]={translated}' + for lang, translated + in translations.items() + ] + + content = content + translations + + # ensure newline at end of file + content = content + ['\n'] + + desktop_fp.write_text('\n'.join(content), encoding='utf-8') + + +def _check_value_length(translations, field): + """Debian GNU/Linux (lintian) limit the length of this field to 80 chars. + """ + LIMIT = 79 + + for lang, val in translations.items(): + if len(val) <= LIMIT: + continue + + print( + f'WARNING: Length of {field}[{lang}] reached the ' + f'limit of {LIMIT} and is {len(val)}. "{val}"' + ) + + +def _set_header(po_path: Path, spdx_base: str): + """Setup the header and comments header of the given po-file to the current + state. + + """ + + pof = polib.pofile(po_path) + + # Version string + pof.metadata['Project-Id-Version'] = f'{PACKAGE_NAME} {PACKAGE_VERSION}' + + copyright = [DEFAULT_COPYRIGHT] + + # Extract authors + e = pof.find(TRANSLATION_PLACEHOLDER_MSGID) + if e: + copyright = copyright + list(filter( + lambda val: len(val) > 0, e.msgstr.split('\n') + )) + for idx, centry in enumerate(copyright): + if not ('(c)' in centry or '©' in centry): + copyright[idx] = f'© {copyright[idx]}' + + copyright = [ + f'SPDX-FileCopyrightText: {centry}' for centry in copyright] + + copyright = '\n'.join(copyright) + + pof.header = f'{copyright}\n{spdx_base}\n{MISSING_TRANSLATORS_TXT}' + + # Remove someday + try: + del pof.metadata['X-Launchpad-Export-Date'] + except KeyError: + pass + + pof.save() + + +def check_existence(): + """Check for existence of essential files. + + Returns: + Nothing if everything is fine. + + Raises: + FileNotFoundError + """ + paths_to_check = [ + LOCAL_DIR, + TEMPLATE_PO + ] + + for file_path in paths_to_check: + if not file_path.exists(): + raise FileNotFoundError(file_path) + + +def update_from_weblate(): + """Translations done on Weblate platform are integrated back into the + repository. + + The Weblate translations live on https://translate.codeberg.org and has + its own internal git repository. This repository is cloned and the + po-files copied into the current local (upstream) repository. + + See comments in code about further details. + """ + + tmp_dir = tempfile.mkdtemp() + + # "Clone" weblate repo into a temporary folder. + # The folder is kept (nearly) empty. No files are transferred except + # the hidden ".git" folder. + cmd = [ + 'git', + 'clone', + # git meta data, only for the first commit + '--depth', + '1', + # don't checkout anything + '--no-checkout', + WEBLATE_URL, + tmp_dir + ] + print(f'Execute "{cmd}".') + run(cmd, check=True) + + # Now checkout po-files from that temporary repository but redirect + # them into the current folder (which is our local upstream repo) instead + # of the temporary repositories folder. + cmd = [ + 'git', + # Use temporary/Weblate repo as checkout source + '--git-dir', f'{tmp_dir}/.git', + 'checkout', + # branch + 'dev', + '--', + 'common/po/*.po' + ] + print(f'Execute "{cmd}".') + run(cmd, check=True) + + shutil.rmtree(tmp_dir, ignore_errors=True) + + +def check_syntax_of_po_files(): + """Check all po files of known syntax violations. + """ + + # Match every character except open/closing curly brackets + rex_reduce = re.compile(r'[^\{\}]') + # Match every pair of curly brackets + rex_curly_pair = re.compile(r'\{\}') + # Extract placeholder/variable names + rex_names = re.compile(r'\{(.*?)\}') + + def _curly_brackets_balanced(to_check): + """Check if curly brackes for variable placeholders are balanced.""" + # Remove all characters that are not curly brackets + reduced = rex_reduce.sub('', to_check) + + # Remove valid pairs of curly brackets + invalid = rex_curly_pair.sub('', reduced) + + # Catch nested curly brackest like this + # "{{{}}}", "{{}}" + # This is valid Python code and won't cause Exceptions. So errors here + # might be false negative. But despite rare cases where this might be + # used it is a high possibility that there is a typo in the translated + # string. BIT won't use constructs like this in strings, so it is + # handled as an error. + if rex_curly_pair.findall(invalid): + print(f'\nERROR ({lang_code}): Curly brackets nested: {to_check}') + return False + + if invalid: + print(f'\nERROR ({lang_code}): Curly brackets not balanced : {to_check}') + return False + + return True + + def _potential_harmful_strings(to_check): + """Check if the translated string contain harmful content. + + URLs indicated by 'href' can be harmful if the string is used in a + QLabel with activated HTML interpretation. + """ + if re.search('href', to_check, re.IGNORECASE): + print(f'CRITICAL - Potential harmful string: "{to_check}"') + return False + + return True + + def _other_errors(to_check): + """Check if there are any other errors that could be thrown via + printing this string.""" + try: + # That is how print() internally parse placeholders and other + # things. + list(string.Formatter().parse(format_string=to_check)) + + except Exception as exc: # pylint: disable=broad-exception-caught + print(f'\nERROR ({lang_code}): {exc} in translation: {to_check}') + return False + + return True + + def _place_holders(trans_string, src_string, tcomments): + """Check if the placeholders between original source string + and the translated string are identical. Order is ignored. + + To disable this check for a specific string add the translation + comment(!) on top of the entry in the po file like this: + + # ignore-placeholder-compare + #: qt/app.py:1961 + #, python-brace-format + msgid "foo" + msgstr "bar" + + Keep in mind that this is a regular comment. It is not a flag (``#, ``) + or a user defined flag (``#. ``). The later two are removed by msgmerge + when updating the po files from the pot file. + """ + + if 'ignore-placeholder-compare' in tcomments: + return True + + flagmsg = 'To disable this check add the comment (not flag!) on ' \ + 'top of the entry in the po-file: ' \ + '"# ignore-placeholder-compare"' + + # Compare number of curly brackets. + for bracket in tuple('{}'): + if src_string.count(bracket) != trans_string.count(bracket): + print(f'\nERROR ({lang_code}): Number of "{bracket}" between ' + 'original source and translated string is different.\n' + f'\nTranslation: {trans_string}\n\n{flagmsg}') + return False + + # Compare variable names + org_names = rex_names.findall(src_string) + trans_names = rex_names.findall(trans_string) + if sorted(org_names) != sorted(trans_names): + print(f'\nERROR ({lang_code}): Names of placeholders between ' + 'original source and translated string are different.\n' + f'\nNames in original : {org_names}\n' + f'\nNames in translation : {trans_names}\n' + f'\nFull translation: {trans_string}\n{flagmsg}') + return False + + return True + + print('Checking syntax of po files…') + + # collect translator-credit string + translators = {} + + # Each po file + for po_path in all_po_files_in_local_dir(): + error_count = 0 + # Language code determined by po-filename + lang_code = po_path.with_suffix('').name + + pof = polib.pofile(po_path) + + # Each translated entry + for entry in pof.translated_entries(): + + # Plural form? + if entry.msgstr_plural or entry.msgid_plural: + # Ignoring plural form because this is to complex, not logical + # in all cases and also not worth the effort. + continue + + if (not _curly_brackets_balanced(entry.msgstr) + or not _potential_harmful_strings(entry.msgstr) + or not _other_errors(entry.msgstr) + or not _place_holders(entry.msgstr, + entry.msgid, + entry.tcomment)): + print(f'\nSource string: {entry.msgid}\n') + error_count += 1 + + # translator string? + if entry.msgid == 'translator-credits-placeholder': + translators[languages.names[lang_code]['en']] \ + = entry.msgstr.split('\n') + + if error_count: + print(f' {lang_code} >> {error_count} errors') + # else: + # print(f' {lang_code} >> OK') + + translators = { + key: translators[key] for key in sorted(translators.keys())} + print('\nTRANSLATORS:') + print(json.dumps(translators, indent=4, ensure_ascii=False)) + + print('') + + +def all_po_files_in_local_dir(): + """All po files (recursive).""" + return LOCAL_DIR.rglob('**/*.po') + + +def all_desktop_files_in_qt_dir(): + return sorted(GUI_DIR.glob('*.desktop')) + + +def show_completeness_info(compl_dict, names_dict): + sorted_dict = dict( + sorted( + compl_dict.items(), + key=lambda items: items[1], + reverse=True + ) + ) + for code, completeness in sorted_dict.items(): + lang = names_dict[code]['en'] + print(f'{completeness:3} % - {lang} ({code})') + + +def create_completeness_dict(): + """Create a simple dictionary indexed by language code and value that + indicate the completeness of the translation in percent. + """ + + print('Calculate completeness for each language in percent…') + + result = {} + + # each po file in the repository + for po_path in all_po_files_in_local_dir(): + pof = polib.pofile(po_path) + + result[po_path.stem] = pof.percent_translated() + + pof.save() + + # "en" is the source language + result['en'] = 100 + + # Sort by language key + result = dict(sorted(result.items())) + + return result + + +def create_languages_py_file(): + """Create the languages.py file containing language names and the + completeness of their translation. + + See the following functions for further details. + - ``update_language_names()`` + - ``create_completeness_dict()`` + """ + + # Convert language names dict to python code as a string + names_dict = update_language_names() + content = ['names = {'] + content.extend(dict_as_code(names_dict, 1)) + content.append('}') + + # the same with completeness dict + compl_dict = create_completeness_dict() + + show_completeness_info(compl_dict, names_dict) + + content.append('') + content.append('') + content.append('completeness = {') + content.extend(dict_as_code(compl_dict, 1)) + content.append('}') + + with LANGUAGE_NAMES_PY.open('w', encoding='utf8') as handle: + + date_now = datetime.datetime.now().strftime('%c') + handle.write(get_spdx_metadata_lines()) + handle.write( + f'#\n# Generated at {date_now} with help\n# of package "babel" ' + 'and "polib".\n') + handle.write('# https://babel.pocoo.org\n') + handle.write('# https://github.com/python-babel/babel\n') + handle.write( + '# pylint: disable=too-many-lines,missing-module-docstring\n') + + handle.write('\n'.join(content)) + handle.write('\n') + + print(f'Result written to {LANGUAGE_NAMES_PY}.') + + # Completeness statistics (English is excluded) + compl = list(compl_dict.values()) + compl.remove(100) # exclude English + statistic = { + 'compl': round(sum(compl) / len(compl)), + 'n': len(compl), + '99_100': len(list(filter(lambda val: val >= 99, compl))), + '90_98': len(list(filter(lambda val: 90 <= val < 99, compl))), + '50_89': len(list(filter(lambda val: 50 <= val <= 89, compl))), + 'lt50': len(list(filter(lambda val: val < 50, compl))) + } + + print('STATISTICS') + print(f'\tTotal completeness: {statistic["compl"]}%') + print(f'\tNumber of languages (excl. English): {statistic["n"]}') + print(f'\t100-99% complete: {statistic["99_100"]} languages') + print(f'\t90-98% complete: {statistic["90_98"]} languages') + print(f'\t50-89% complete: {statistic["50_89"]} languages') + print(f'\tless than 50% complete: {statistic["lt50"]} languages') + + +def create_language_names_dict(language_codes: list) -> dict: + """Create dict of language names in different flavors. + The dict is used in the LanguageDialog to display the name of + each language in the UI's current language and the language's own native + representation. + """ + + # We keep this import local because it is a rare case that this function + # will be called. This happens only if a new language is added to BIT. + try: + # pylint: disable-next=import-outside-toplevel + import babel + except ImportError as exc: + raise ImportError( + 'Can not import package "babel". Please install it.') from exc + + # Babel minimum version (because language code "ie") + from packaging.version import Version + if Version(babel.__version__) < Version('2.15'): + raise ImportError( + f'Babel version 2.15 required. But {babel.__version__} ' + 'is installed.') + + # Source language (English) should be included + if 'en' not in language_codes: + language_codes.append('en') + + # Don't use defaultdict because pprint can't handle it + result = {} + + for code in sorted(language_codes): + # print(f'Processing language code "{code}"…') + + lang = babel.Locale.parse(code) + result[code] = {} + + # Native name of the language + # e.g. 日本語 + result[code]['_native'] = lang.get_display_name(code) + + # Name of the language in all other foreign languages + # e.g. Japanese, Japanisch, ... + for foreign in language_codes: + result[code][foreign] = lang.get_display_name(foreign) + + return result + + +def update_language_names() -> dict: + """See `create_language_names_dict() for details.""" + + # Languages code based on the existing po-files + langs = [po_path.stem for po_path in LOCAL_DIR.rglob('**/*.po')] + + # Some languages missing in the list of language names? + try: + missing_langs = set(langs) - set(languages.names) + except AttributeError: + # Under circumstances the languages file is empty + missing_langs = ['foo'] + + if missing_langs: + print('Create new language name list because of missing ' + f'languages: {missing_langs}') + + return create_language_names_dict(langs) + + return languages.names + + +def get_shortcut_entries(po_file: polib.POFile) -> list[polib.POEntry]: + """Return list of po-file entries using a shortcut indicator ("&") + and are not obsolete. + """ + result = filter(lambda entry: entry.obsolete == 0 and + REX_SHORTCUT_LETTER.search(entry.msgid), po_file) + + return list(result) + + +def get_shortcut_groups() -> dict[str, list]: + """Return the currently used "shortcut groups" and validate if they are + up to date with the source strings in "messages.pot". + + Returns: + A dictionarie indexed by group names with list of source strings. + + Raises: + ValueError: If the shortcut indicator using source strings are + modified. + """ + + # Get all entries using a shortcut indicator + real = get_shortcut_entries(polib.pofile(TEMPLATE_PO)) + # Reduce to their source strings + real = [entry.msgid for entry in real] + + # Later this list is sliced into multiple groups + expect = [ + # Main window (menu bar) + '&Backup', + '&Restore', + '&Help', + 'Back In &Time', + # Manage profiles dialog (tabs) + '&General', + '&Include', + '&Exclude', + '&Remove & Retention', + '&Options', + 'E&xpert Options', + ] + + # Plausibility check: + # Difference between the real and expected strings indicate + # modifications in the GUI and in the shortcut groups. + if not sorted(real) == sorted(expect): + # This will happen when the source strings are somehow modified or + # some strings add or removed. + # SOLUTION: Look again into the GUI and its commit history what was + # modified. Update the "expect" list to it. + raise ValueError( + f'Source strings with GUI shortcuts in {TEMPLATE_PO} are not as ' + 'expected.\n' + f' Expected: {sorted(expect)}\n' + f' Real: {sorted(real)}') + + return {'mainwindow': expect[:4], 'manageprofile': expect[4:]} + + +def check_shortcuts(): + """Check for redundant used letters as shortcut indicators in translated + GUI strings. + + Keyboard shortcuts are indicated via the & in front of a character + in a GUI string (e.g. a button or tab). For example "B&ackup" can be + activated with pressing ALT+A. As another example the strings '&Exclude' + and '&Export' used in the same area of the GUI won't work because both of + them indicate the 'E' as a shortcut. They need to be unique. + + These situation can happen in translated strings in most cases translators + are not aware of that feature or problem. It is nearly impossible to + control this on the level of the translation platform. + """ + + groups = get_shortcut_groups() + + # each po file in the repository + for po_path in list(LOCAL_DIR.rglob('**/*.po')): + + print(f'******* {po_path} *******') + + # Remember shortcut relevant entries. + real = {key: [] for key in groups} + + # # WORKAROUND. See get_shortcut_groups() for details. + # real['mainwindow'].append('Back In &Time') + + # Entries using shortcut indicators + shortcut_entries = get_shortcut_entries(polib.pofile(po_path)) + + # Group the entries to their shortcut groups + for entry in shortcut_entries: + for groupname in real: + if entry.msgid in groups[groupname]: + real[groupname].append(entry.msgstr) + + # Each shortcut group... + for groupname in real: + + # All shortcut letters used in that group + letters = '' + + # Collect letters + for trans in real[groupname]: + try: + letters = letters \ + + REX_SHORTCUT_LETTER.search(trans).groups()[0] + except AttributeError: + pass + + # Redundant shortcuts? set() do remove duplicates + if len(letters) > len(set(letters)): + err_msg = f'Maybe redundant shortcuts in "{po_path}".' + + # Missing shortcuts in translated strings? + if len(letters) < len(real[groupname]): + err_msg = err_msg + ' Maybe missing ones.' + + err_msg = f'{err_msg} Please take a look.\n' \ + f' Group: {groupname}\n' \ + f' Source: {groups[groupname]}\n' \ + f' Translation: {real[groupname]}' + + print(err_msg) + + +def get_spdx_metadata_lines(ignore_copyright: bool = False, + without_comment_prefix: bool = False) -> str: + """Extract the SPDX meta data lines from the current source file.""" + result = '' + + with Path(__file__).open('r') as handle: + + for line in handle: + + # ignore shebang + if line.startswith('#!'): + continue + + # ignore copyright + if ignore_copyright and 'SPDX-FileCopyrightText' in line: + continue + + # stop + if line.startswith('"""') or line.startswith('import'): + break + + # remove comments prefix "# " + if without_comment_prefix and line.startswith('#'): + line = line[1:] + + result = result + line.strip() + '\n' + + return result + + +def _get_translation_for_desktop_string(value: str) -> dict[str, str]: + """Check all po files for a translation of 'value' and create a list + of the results fitting to a desktop file. + + e.g. + GenericName[de]=Foo + GenericName[vi]=Bar + + Returns: + A dictionary indexed by language code and the translation as value. + """ + translations: dict[str, str] = {} + + for po_path in LOCAL_DIR.rglob('**/*.po'): + po = polib.pofile(po_path) + + entry = po.find(value) + + # Nothing found or no translation + if entry is None or not entry.msgstr: + continue + + # Translation finished? + if 'fuzzy' in entry.flags or entry.obsolete: + continue + + translations[po_path.stem] = entry.msgstr + + # sort by Key + translations = dict(sorted(translations.items())) + + return translations + + +if __name__ == '__main__': + check_existence() + + FIN_MSG = 'Please check the result via "git diff" before committing.' + + # Scan python source files for translatable strings + if 'source' in sys.argv: + update_po_template() + update_po_language_files('--remove-obsolete-entries' in sys.argv) + update_desktop_files() + create_languages_py_file() + print(FIN_MSG) + sys.exit() + + # Download translations (as po-files) from Weblate and integrate them + # into the repository. + if 'weblate' in sys.argv: + update_from_weblate() + check_syntax_of_po_files() + create_languages_py_file() + print(FIN_MSG) + sys.exit() + + # Check for redundant &-shortcuts + if 'shortcuts' in sys.argv: + check_shortcuts() + sys.exit() + + # Check for syntax problems (also implicit called via "weblate") + if 'syntax' in sys.argv: + check_syntax_of_po_files() + sys.exit() + + print('Use one of the following argument keywords:\n' + ' source - Update the pot and po files with translatable ' + 'strings extracted from py files. (Prepare upload to Weblate). ' + 'Optional use --remove-obsolete-entries\n' + ' weblate - Update the po files with translations from ' + 'external translation service Weblate. (Download from Weblate)\n' + ' shortcuts - Check po files for redundant keyboard shortcuts ' + 'using "&"\n' + ' syntax - Check syntax of po files. (Also done via "weblate" ' + 'command)') + + sys.exit(1) diff --git a/updatecopyright.sh b/updatecopyright.sh deleted file mode 100755 index 399a5818b..000000000 --- a/updatecopyright.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -find ./ -type f \ - ! -wholename "./common/po/*" \ - ! -wholename "./.git/*" \ - -exec sed \ - -e "/Germar Reitze/s/[cC]opyright ([cC]) \([0-9]*\)-\([0-9]*\) /Copyright (C) \1-$(date +%Y) /g" \ - -e "/Germar Reitze/s/[cC]opyright ([cC]) \([0-9]*\) /Copyright (C) \1-$(date +%Y) /g" \ - -i {} + diff --git a/updateversion.sh b/updateversion.sh index 2b8a0eb2a..58c0cc1f1 100755 --- a/updateversion.sh +++ b/updateversion.sh @@ -1,59 +1,63 @@ #!/bin/bash - +# SPDX-FileCopyrightText: © 2008 Oprea Dan +# SPDX-FileCopyrightText: © 2012 Germar Reitze +# SPDX-FileCopyrightText: © 2022 Jürgen Altfeld (aryoda) +# SPDX-FileCopyrightText: © 2023 Christian Buhtz +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of the program "Back In time" which is released under GNU +# General Public License v2 (GPLv2). See LICENSES directory or go to +# . + +# Updates all version numbers using the VERSION file +# and creates a new DEBIAN changelog file for this version +# by extracting the changes of this version from the +# CHANGES file. +# +# Development notes (May '23, Buhtz): +# Should be treated as a workaround that will get replaced in the future. +# Handling of version numbers and other package metadata can be done very +# elegant and centralized within the Python Packaging process (e.g. using +# pyproject.toml and additional tools. +# Handling of Debian (and PPA) related stuff will be separated from that +# upstream repo because it is distro specific. + +# Outdated TODOs: +# TODO Requires refactoring and adjustments to separate +# - the update of version numbers +# - from the preparation of a new DEBIAN package release +# since version updates must be possible without +# a DEBIAN package release. +# +# TODO The version number must still be maintained in two places +# (despite this script): +# 1. File "VERSION" +# 2. As headline in the file "CHANGES" +# If those two numbers do not match the script does +# not extract the correct changes of the version from the CHANGES file. +# +# TODO The name of this script file is misleading (find a better one) +# TODO Make sure this script works idempotent (multiple calls = same result) +# TODO This script does not update release dates scattered around in +# different files (eg. common/man/C/backintime.1 line 1) VERSION=`cat VERSION` -echo VERSION: $VERSION +VERSION_WITHOUT_BRANCH=$VERSION -if [ "x$USER" = "xgermar" ]; then - MAINTAINER="Germar Reitze " -else - MAINTAINER="BIT Team " +if [[ $VERSION == *-dev ]] +then + VERSION+="."`git rev-parse --short HEAD` fi -update_config () { - echo "Update '$1'" - sed -e "s/^\(\s*\)VERSION = '.*'$/\1VERSION = '$VERSION'/" \ - -i $1 -} - -update_man_page () { - echo "Update '$1'" - sed -e "s/\.TH\(.*\)\"version\([^\"]*\)\"\(.*\)$/.TH\1\"version $VERSION\"\3/" \ - -i $1 -} - -update_omf () { - echo "Update '$1'" - sed -e "s/^\([ \]*\)$//" \ - -e "s/^$//" \ - -i $1 -} +# MAINTAINER="Germar Reitze " +# MAINTAINER="BIT Team " +MAINTAINER="BIT Team " -update_changelog () { +update_app_version () { echo "Update '$1'" - echo "backintime ($VERSION) unstable; urgency=low" > $1 - cat CHANGES | awk 'BEGIN {ins=0} /^Version '$VERSION'/ {ins=1; next} /^Version [0-9.]+/ && (ins == 1) {exit 0} /^\*/ && (ins == 1) {print " "$0}' >> $1 - if [ $(cat $1 | wc -l) -eq 1 ]; then - echo " * prepair next version" >> $1 - fi - echo " -- ${MAINTAINER} $(date -R)" >> $1 + sed --expression="s/^\(\s*\)__version__ = '.*'$/\1__version__ = '$VERSION'/" --in-place $1 } - -update_config common/config.py - -update_man_page common/man/C/backintime.1 - -update_man_page common/man/C/backintime-config.1 - -update_man_page common/man/C/backintime-askpass.1 - -update_man_page qt/man/C/backintime-qt.1 - -update_xml qt/docbook/en/index.docbook - -update_changelog debian/changelog +update_app_version common/version.py